// ==UserScript== // @name Discuz! ·快捷回复助手 // @namespace http://tampermonkey.net/ // @version 3.1 // @description 为Discuz!论坛添加快捷回复工具栏,支持常用短语、随机短语、分类快捷语,界面美观,适配吾爱破解等等 // @author Enhanced // @match *://*.52pojie.cn/* // @match *://*.chinaz.com/* // @match *://*.ithome.com/* // @match *://*.nga.cn/* // @match *://*.nga.178.com/* // @match *://*.tieba.com/* // @match *://*.kafan.cn/* // @match *://*.bilibili.com/* // @match *://*.hackbase.com/* // @match *://*.dospy.com/* // @match *://*.xda-developers.com/* // @match *://*.android.com/* // @match *://*.miui.com/* // @match *://*.flyme.cn/* // @match *://*.zol.com.cn/* // @match *://*.pcbeta.com/* // @match *://*.it168.com/* // @match *://*.pconline.com.cn/* // @match *://*.cnki.net/* // @match *://*.cnblogs.com/* // @match *://*.csdn.net/* // @match *://*.oschina.net/* // @match *://*.gitbooks.io/* // @match *://*.tianya.cn/* // @match *://*.mop.com/* // @match *://*.douban.com/* // @match *://*.zhihu.com/* // @match *://*.hupu.com/* // @match *://*.dongqiudi.com/* // @match *://*.qiumiwu.com/* // @match *://*.a9vg.com/* // @match *://*.tgbus.com/* // @match *://*.3dmgame.com/* // @match *://*.gamersky.com/* // @match *://*.ali213.net/* // @match *://*.游民星空.com/* // @match *://*.uuu9.com/* // @match *://*.52pk.com/* // @match *://*.duowan.com/* // @match *://*.178.com/* // @match *://*.多玩.com/* // @match *://*.766.com/* // @match *://*.uuu9.com/* // @match *://*.52miji.com/* // @match *://*.52破解.com/* // @match *://*.52pojie.net/* // @match *://*.52pojie.org/* // @match *://*.52破解.org/* // @match *://*.52破解.net/* // @match *://*/bbs/* // @match *://*/forum/* // @match *://*/discuz/* // @match *://*/community/* // @match *://*/thread-* // @match *://*/viewthread.php* // @match *://*/forum.php* // @match *://*/post.php* // @match *://*/reply.php* // @match *://*/newreply.php* // @match *://*/newthread.php* // @match *://*/editpost.php* // @icon https://www.52pojie.cn/favicon.ico // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // ========== 配置区 ========== const COMMON_PHRASES = [ '感谢分享', '谢谢楼主', '支持一下', '看看隐藏', '学习学习', '顶一下', '收藏了', '好东西', '热心回复', '我很赞同' ]; // 分类短语(带分类标签) const PHRASES_WITH_CATEGORY = [ // 感谢类 { text: '正需要,谢谢分享', category: '感谢', emoji: '🙏' }, { text: '感谢发布原创内容', category: '感谢', emoji: '😊' }, { text: '谢谢大佬分享', category: '感谢', emoji: '👍' }, { text: '感谢热心解答', category: '感谢', emoji: '❤️' }, { text: '感谢分享资源', category: '感谢', emoji: '🎁' }, // 支持类 { text: '顶一个,让更多人看到', category: '支持', emoji: '💪' }, { text: '已收藏,备用', category: '支持', emoji: '⭐' }, { text: '先mark一下', category: '支持', emoji: '📌' }, { text: '前排支持', category: '支持', emoji: '🆙' }, { text: '支持原创', category: '支持', emoji: '✨' }, // 学习类 { text: '学习了,很有帮助', category: '学习', emoji: '📚' }, { text: '硬核内容,学习了', category: '学习', emoji: '🧐' }, { text: '这个帖子干货满满', category: '学习', emoji: '📖' }, { text: '涨知识了', category: '学习', emoji: '🎓' }, { text: '这个方法不错', category: '学习', emoji: '💡' }, // 通用类 { text: '看看隐藏内容', category: '通用', emoji: '👀' }, { text: '先看看怎么样', category: '通用', emoji: '🔍' }, { text: '留个脚印', category: '通用', emoji: '💬' }, { text: '看起来不错', category: '通用', emoji: '🉑' }, { text: '每日一看', category: '通用', emoji: '⏰' }, // 赞扬类 { text: '楼主好人,一生平安', category: '赞扬', emoji: '👏' }, { text: '这个真的很实用', category: '赞扬', emoji: '🌟' }, { text: '说到点子上了', category: '赞扬', emoji: '🎯' }, { text: '非常赞同', category: '赞扬', emoji: '💯' }, { text: '优质内容', category: '赞扬', emoji: '🏆' }, // 幽默类 { text: '我来了,你在哪', category: '幽默', emoji: '😝' }, { text: '让我研究研究', category: '幽默', emoji: '🤔' }, { text: '好耶,感谢分享', category: '幽默', emoji: '🥳' }, { text: '收藏从未停止', category: '幽默', emoji: '😎' }, { text: '先顶后看', category: '幽默', emoji: '🤪' } ]; // 纯文本短语数组(用于随机) const ALL_PHRASES = [ ...COMMON_PHRASES, ...PHRASES_WITH_CATEGORY.map(p => p.text) ]; // ============================================== /** * 向 textarea 插入文本 */ function insertText(textarea, text) { if (!textarea) return; textarea.focus(); if (typeof textarea.selectionStart === 'number') { const start = textarea.selectionStart; const end = textarea.selectionEnd; const scrollTop = textarea.scrollTop; const newValue = textarea.value.substring(0, start) + text + textarea.value.substring(end); textarea.value = newValue; const newPos = start + text.length; textarea.selectionStart = newPos; textarea.selectionEnd = newPos; textarea.scrollTop = scrollTop; } else { textarea.value += text; } const event = new Event('input', { bubbles: true }); textarea.dispatchEvent(event); } /** * 获取随机短语(50%概率带表情) */ function getRandomPhrase() { // 随机决定是否带表情 const withEmoji = Math.random() > 0.5; if (withEmoji) { // 从分类短语中随机选一个(带表情) const randomItem = PHRASES_WITH_CATEGORY[Math.floor(Math.random() * PHRASES_WITH_CATEGORY.length)]; return `${randomItem.emoji} ${randomItem.text}`; } else { // 从所有短语中随机选一个(不带表情) return ALL_PHRASES[Math.floor(Math.random() * ALL_PHRASES.length)]; } } /** * 创建分类下拉菜单 */ function createCategorySelect(textarea) { const select = document.createElement('select'); select.className = 'category-select'; // 默认选项 const defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = '📋 按分类选择...'; select.appendChild(defaultOption); // 按分类分组 const categories = {}; PHRASES_WITH_CATEGORY.forEach(item => { if (!categories[item.category]) { categories[item.category] = []; } categories[item.category].push(item); }); // 为每个分类创建选项组 Object.keys(categories).forEach(category => { const optgroup = document.createElement('optgroup'); optgroup.label = getCategoryEmoji(category) + ' ' + category; categories[category].forEach(item => { const option = document.createElement('option'); option.value = `${item.emoji} ${item.text}`; option.textContent = `${item.emoji} ${item.text}`; optgroup.appendChild(option); }); select.appendChild(optgroup); }); select.addEventListener('change', function() { const text = this.value; if (text) { insertText(textarea, text); this.selectedIndex = 0; } }); return select; } /** * 获取分类表情 */ function getCategoryEmoji(category) { const emojiMap = { '感谢': '🙏', '支持': '💪', '学习': '📚', '通用': '📌', '赞扬': '👏', '幽默': '😄' }; return emojiMap[category] || '📋'; } /** * 为指定的 textarea 创建快捷工具栏 */ function createToolbar(textarea) { if (textarea.dataset.qrInitialized) return null; textarea.dataset.qrInitialized = 'true'; const toolbar = document.createElement('div'); toolbar.className = 'quick-reply-toolbar'; // 左侧:常用短语按钮 const leftSection = document.createElement('div'); leftSection.className = 'toolbar-section left-section'; COMMON_PHRASES.forEach(phrase => { const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'quick-btn'; btn.textContent = phrase; btn.addEventListener('click', (e) => { e.preventDefault(); insertText(textarea, phrase); }); leftSection.appendChild(btn); }); toolbar.appendChild(leftSection); // 中间:随机按钮 const centerSection = document.createElement('div'); centerSection.className = 'toolbar-section center-section'; const randomBtn = document.createElement('button'); randomBtn.type = 'button'; randomBtn.className = 'random-btn'; randomBtn.innerHTML = '🎲 随机一句'; randomBtn.addEventListener('click', (e) => { e.preventDefault(); const randomPhrase = getRandomPhrase(); // 清空textarea textarea.value = ''; // 插入随机短语 insertText(textarea, randomPhrase); }); centerSection.appendChild(randomBtn); toolbar.appendChild(centerSection); // 右侧:分类下拉菜单 const rightSection = document.createElement('div'); rightSection.className = 'toolbar-section right-section'; const categorySelect = createCategorySelect(textarea); rightSection.appendChild(categorySelect); toolbar.appendChild(rightSection); // 将工具栏插入到 textarea 之前 textarea.parentNode.insertBefore(toolbar, textarea); return toolbar; } /** * 初始化页面中所有符合条件的 textarea */ function initQuickReplyBars() { const textareas = document.querySelectorAll( 'textarea[name="message"], ' + 'textarea[id="fastpostmessage"], ' + 'textarea[id="message"]' ); textareas.forEach(textarea => { if (textarea.disabled || textarea.readOnly) return; createToolbar(textarea); }); } // ----- 样式定义(优化后的界面)----- const style = document.createElement('style'); style.textContent = ` .quick-reply-toolbar { background: linear-gradient(to bottom, #f8f9fa, #e9ecef); border: 1px solid #ced4da; border-radius: 8px; padding: 10px 12px; margin: 8px 0 12px 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; font-size: 13px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; box-shadow: 0 2px 4px rgba(0,0,0,0.05); width: 100%; box-sizing: border-box; z-index: 999; } .toolbar-section { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; } .left-section { flex: 2; min-width: 300px; } .center-section { flex: 0 0 auto; } .right-section { flex: 1; min-width: 200px; justify-content: flex-end; } .quick-btn { background: white; border: 1px solid #adb5bd; border-radius: 20px; padding: 5px 12px; cursor: pointer; color: #495057; font-size: 13px; line-height: 1.4; transition: all 0.2s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.05); white-space: nowrap; } .quick-btn:hover { background: #007bff; border-color: #007bff; color: white; transform: translateY(-1px); box-shadow: 0 4px 6px rgba(0,123,255,0.2); } .quick-btn:active { transform: translateY(0); box-shadow: none; } .random-btn { background: linear-gradient(135deg, #6f42c1, #007bff); border: none; border-radius: 25px; padding: 7px 20px; cursor: pointer; color: white; font-size: 14px; font-weight: 500; transition: all 0.3s ease; box-shadow: 0 4px 10px rgba(111,66,193,0.3); white-space: nowrap; display: flex; align-items: center; gap: 5px; } .random-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 15px rgba(111,66,193,0.4); background: linear-gradient(135deg, #5a32a3, #0056b3); } .random-btn:active { transform: translateY(0); box-shadow: 0 2px 5px rgba(111,66,193,0.3); } .category-select { padding: 7px 12px; border-radius: 25px; border: 1px solid #adb5bd; background: white; font-size: 13px; cursor: pointer; min-width: 180px; max-width: 100%; box-shadow: 0 1px 3px rgba(0,0,0,0.05); transition: all 0.2s ease; appearance: none; background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 10px center; background-size: 15px; } .category-select:hover { border-color: #007bff; box-shadow: 0 2px 5px rgba(0,123,255,0.1); } .category-select optgroup { font-weight: 600; color: #495057; background: #f8f9fa; } .category-select option { padding: 5px 10px; font-weight: normal; } /* 适应移动端 */ @media (max-width: 768px) { .quick-reply-toolbar { flex-direction: column; align-items: stretch; } .toolbar-section { width: 100%; justify-content: center; } .left-section { min-width: auto; } .right-section { min-width: auto; } .quick-btn { font-size: 12px; padding: 4px 10px; } .category-select { width: 100%; } } /* 暗色主题适配(如果论坛有暗色模式) */ @media (prefers-color-scheme: dark) { .quick-reply-toolbar { background: linear-gradient(to bottom, #2d3238, #1e2227); border-color: #40474f; } .quick-btn { background: #3a4048; border-color: #4f5a65; color: #e9ecef; } .quick-btn:hover { background: #007bff; border-color: #007bff; color: white; } .category-select { background-color: #3a4048; border-color: #4f5a65; color: #e9ecef; } } `; document.head.appendChild(style); // ----- 监听动态添加的回复框 ----- const observer = new MutationObserver(() => { initQuickReplyBars(); }); observer.observe(document.body, { childList: true, subtree: true }); // ----- 初始执行 ----- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initQuickReplyBars); } else { initQuickReplyBars(); } })();