Greasy Fork

Greasy Fork is available in English.

牛牛聊天发图片插件

让牛牛聊天支持发送图片,发送任何外链图片链接时,自动转换为图片,可以点击查看大图

目前为 2025-05-12 提交的版本。查看 最新版本

// ==UserScript==
// @name         牛牛聊天发图片插件
// @namespace    https://www.milkywayidle.com/
// @version      0.1.2
// @description  让牛牛聊天支持发送图片,发送任何外链图片链接时,自动转换为图片,可以点击查看大图
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @grant        GM_addStyle
// @license MIT
// ==/UserScript==

(function() {
	'use strict';

	GM_addStyle(`
        .chat-img {
            display: inline-flex;
            margin: 1px 4px;
            max-height: 60px;
            max-width: 100px;
            width: fit-content;
            border: 2px solid #778be1;
            border-radius: 4px;
            padding: 1px;
            white-space: nowrap;
            background: #000;
            cursor: pointer;
        }
        .chat-img-preview {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
            cursor: zoom-out;
        }
        .chat-img-preview img {
            max-width: 90%;
            max-height: 90%;
            border: 2px solid #fff;
            border-radius: 4px;
        }
    `);

	const chatHistorySelector = 'div.ChatHistory_chatHistory__1EiG3';
	const chatMessageSelector = 'div.ChatMessage_chatMessage__2wev4';

	function isImageUrl(url) {// 检查链接是否是图片
		return url && /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i.test(url);
	}

	function createPreviewOverlay(imgSrc) {//创建预览
		const overlay = document.createElement('div');
		overlay.className = 'chat-img-preview';
		const previewImg = document.createElement('img');
		previewImg.src = imgSrc;
		overlay.appendChild(previewImg);
		document.body.appendChild(overlay);
		
		overlay.addEventListener('click', (e) => {// 点击后关闭图片预览
			if (e.target === overlay || e.target === previewImg) {
				document.body.removeChild(overlay);
			}
		});
		document.addEventListener('keydown', function handleEsc(e) {// ESC关闭图片预览
			if (e.key === 'Escape') {
				document.body.removeChild(overlay);
				document.removeEventListener('keydown', handleEsc);
			}
		});
	}
	function replaceLinkContentWithImage(link) {//修改A标签内的图片
		const href = link.getAttribute('href');
		if (!isImageUrl(href)) return;
		if (link.querySelector('.chat-img')) return;

		link.innerHTML = '';
		const img = document.createElement('img');
		img.src = href;
		img.className = 'chat-img';
		link.appendChild(img);

		
		const newLink = link.cloneNode(true);//移除所有现有的事件监听器,用于屏蔽聊天URL确认框(@guch8017 提交)
		link.parentNode.replaceChild(newLink, link);

		newLink.addEventListener('click', (e) => {//改为插件的点击处理
			e.preventDefault();
			e.stopImmediatePropagation();
			createPreviewOverlay(href);
		});
	}
	function processExistingMessages(container) {//聊天页面消息处理
		const messages = container.querySelectorAll(chatMessageSelector);
		messages.forEach(msg => {
			const links = msg.querySelectorAll('a');
			links.forEach(replaceLinkContentWithImage);
		});
	}
	function observeChatHistory(chatHistory) {//监听聊天页面变化
		processExistingMessages(chatHistory);
		const observer = new MutationObserver(mutations => {
			mutations.forEach(mutation => {
				mutation.addedNodes.forEach(node => {
					if (node.nodeType === Node.ELEMENT_NODE) {
						const messages = node.matches(chatMessageSelector) ? [node] : node
							.querySelectorAll(chatMessageSelector);
						messages.forEach(msg => {
							const links = msg.querySelectorAll('a');
							links.forEach(replaceLinkContentWithImage);
						});
					}
				});
			});
		});
		observer.observe(chatHistory, {
			childList: true,
			subtree: true
		});
	}
	function init() {
		const chatHistories = document.querySelectorAll(chatHistorySelector);
		if (chatHistories.length === 0) {
			setTimeout(init, 1000);
			return;
		}
		chatHistories.forEach(observeChatHistory);
		const globalObserver = new MutationObserver(mutations => {
			mutations.forEach(mutation => {
				mutation.addedNodes.forEach(node => {
					if (node.nodeType === Node.ELEMENT_NODE) {
						const newHistories = node.querySelectorAll?.(chatHistorySelector) || [];
						newHistories.forEach(observeChatHistory);
					}
				});
			});
		});
		globalObserver.observe(document.body, {
			childList: true,
			subtree: true
		});
	}
	init();
})();