包含功能有:聊天室辅助。
// ==UserScript== // @name 狼之乐园辅助 // @namespace https://greasyfork.org/users/159546 // @version 1.0.4 // @description 包含功能有:聊天室辅助。 // @author LEORChn // @include *://wolfbbs.net/* // @run-at document-start // @grant GM_xmlHttpRequest // @grant GM_notification // @connect translate.google.cn // ==/UserScript== registerPrototype(); setTimeout(function(){ GM_notification({ title: '通知已打开', text: '此消息在60秒后过期。\n'+new Date().getTime(), timeout: 60000, image: 'http://wolfbbs.net/favicon.ico' }); }, 2000); var IntervalTimeout = 1000; function onInterval(){ // daemon ChatRoom(); } var chatwindow = null, ChatWindowWH = [770, 530], ID_LEORCHN_WOLFBBS_CHATROOM_CSS = 'leorchn_chatroom_css', ID_LEORCHN_WOLFBBS_CHATROOM_CSS_MORE_FUNCTION = 'leorchn_chatroom_css_more_function', ID_LEORCHN_WOLFBBS_CHATROOM_MORE_FUNCTION = 'leorchn_chatroom_more_function'; function ChatRoom(){ Notification_init(); var lpn = location.pathname; if(lpn == '/' || lpn.startsWith('/forum.php')){ var a = $('#wgo_chat a'); if(!a) return; a.onclick = function(){ if(chatwindow){ if(!chatwindow.closed && !confirm('您已经打开了一个聊天窗口!\n\n要重新打开吗?')) return false; if(!chatwindow.closed) chatwindow.close(); } chatwindow = window.open('/chat.php', '_blank', 'location=no,resizable=no,width='+ChatWindowWH[0]+',height='+ChatWindowWH[1]+',left='+window.screenX+',top='+window.screenY); window.cwin = chatwindow; return false; }; }else if(lpn.startsWith('/chat.php')){ ChatRoomImpl_injectCss(); ChatRoomImpl_injectMoreFunction(); ChatRoomImpl_testNewMessage(); } } function ChatRoomImpl_injectCss(){ if(fv(ID_LEORCHN_WOLFBBS_CHATROOM_CSS)) return; var cssdiv = ct('style#'+ID_LEORCHN_WOLFBBS_CHATROOM_CSS), css = 'html body{ width:100vw; min-width:770px; overflow:hidden; background-position-x:-20px; background:rgb(247,246,228) }' + 'body>div.body_wrapper{ width:100vw; padding:0; margin:0 }' + 'body>.above_body,' + '#breadcrumb, form#notices, #pagetitle, #pfc_minmax, #pfc_title, #wgo, #footer, .below_body{ display:none }' + // 隐藏元素 'div.body_wrapper>#pfc_container{ position:absolute; top:0; padding:0; z-index:-9 }' + // 外边框:去除padding '#pfc_channels_content .pfc_online{ width:170px }' + // 在线列表:最小宽度 '#pfc_channels_content .pfc_chat{ width:calc(100% - 170px) }' + // 聊天窗:由在线列表减去的宽度 '#pfc_input_container{ position:relative }' + // 调整输入框及其之后的布局方法 '#pfc_input_container>table:first-child{ max-width:calc(100% - 175px) }' + // 限制聊天输入框大小 '#pfc_input_container #pfc_words{ width:calc(100% - 20px); height:100%; padding:0 10px; border-radius:20px }' + // 美化聊天输入框 '#pfc_input_container>#pfc_cmd_container{ position:absolute; width:170px; height:40px; right:2px; top:0 }' + // 表情等功能按钮设置位置 '#pfc_input_container #pfc_logo{ position:absolute; bottom:0 }' + // 调整聊天组件(PFC)的LOGO位置 '#pfc_input_container #pfc_ping{ position:absolute; bottom:0; right:0; margin:0; padding:0 5px; background:rgb(251,244,225) }' + // 调整延迟值显示位置 '#pfc_container #pfc_errors:not(:empty){ position:fixed; left:0; top:0; width:100vw; line-height:100vh; background:rgba(0,0,0,0.5);font-weight:bold; font-style:normal; text-align:center; font-size:24px; z-index:999}' + // 错误信息样式 "#pfc_errors:after{ content:' '; position:absolute; width:60vw; height:40vh; left:20vw; top:30vh; background:#fec; z-index:-9999 }" + // 错误信息样式:新对话框界面 '#pfc_content_expandable>#pfc_smileys{ position:absolute; bottom:0px; right:0px; width:166px; padding:0 0 3px; margin-bottom:55px; z-index:99 }' + // 表情选择栏 "#pfc_smileys:after{ content:' '; position:absolute; display:block; width:10px; height:10px; right:74px; bottom:-5px; transform:rotate(45deg); z-index:-1; background:#fff }"; // 表情选择栏的小箭头 cssdiv.innerText = css; document.head.appendChild(cssdiv); } function ChatRoomImpl_injectMoreFunction(){ var root = fv('pfc_bbcode_container'); if(fv(ID_LEORCHN_WOLFBBS_CHATROOM_MORE_FUNCTION)) return; if(!root) return; var divid = '#' + ID_LEORCHN_WOLFBBS_CHATROOM_MORE_FUNCTION, cssdiv = ct('style#' + ID_LEORCHN_WOLFBBS_CHATROOM_CSS_MORE_FUNCTION), css = divid + '{ position:relative }' + // 按钮样式 divid + '>div{ position:absolute; bottom:calc(100% + 10px); width:100px; padding:5px; transform:translateX(-25%); background:#fff; border:#000 solid 1px; z-index:99 }' + // 扩展界面样式 divid + ">div:after{ content:' '; position:absolute; display:block; width:10px; height:10px; left:50%; bottom:-6px; transform:translateX(-50%) rotate(45deg); z-index:-2; background:#fff; border:#000 solid 1px; border-top:none; border-left:none }" + // 扩展界面的对话框小箭头 divid + ':not(:hover)>div{ display:none }' + // 鼠标离开按钮和扩展界面后,扩展界面消失 divid + ":hover:after{ content:' '; position:absolute; display:block; width:100%; height:100%; bottom:100% }" + // 在按钮和扩展界面之间搭建一个隐形界面,使用户在将鼠标从按钮移动到扩展界面的过程中,不被判定为“离开按钮和扩展界面”,从而防止扩展界面消失 divid + ' a{ cursor:pointer }'; // 由于 a标签 没有 href属性 会使用默认指针样式,变为手形 cssdiv.innerText = css; document.head.appendChild(cssdiv); var ChatRoomTest_base = function(e){ var root = $('#pfc_channels_content .pfc_chat'), pack = ct('div'), msg = ct('div.pfc_cmd_send pfc_message pfc_evenmsg'), date = ct('span.pfc_date', '13/03/2020'), time = ct('span.pfc_heure', '20:40:39'), nick = ct('span.pfc_nick', '<'), nick_marker = ct('span.pfc_nickmarker', '其他人'); pack.appendChild(msg); e.className = 'pfc_words'; [date, time, nick, e].forEach(function(e){ msg.appendChild(e); }); nick.appendChild(nick_marker); nick.appendChild(document.createTextNode('>')); root.appendChild(pack); }, ChatRoomTest_addImgText = function(){ var span = ct('span'), a = ct('a'), img = ct('img'); img.src = 'http://wolfbbs.net/phpfreechat/showimage.php?image=1583985191.png&t=1'; span.appendChild(a); a.appendChild(img); span.appendChild(document.createTextNode('我这边可以打开')); ChatRoomTest_base(span); }, ChatRoomTest_addText = function(){ var span = ct('span'); span.appendChild(document.createTextNode('@狼王白牙, 推特改造的话。。做过类似的,但是改得没那么好。你详细说说看你想怎么改?关闭显示 “你可能会喜欢” “趋势” 应该会挺简单的我觉得')); ChatRoomTest_base(span); }, ChatRoomTest_addTextEmoticonText = function(){ var span = ct('span'), txt1 = document.createTextNode('不知道 › @LEORChn 的推特使用习惯如何'), emo1 = ct('img'), txt2 = document.createTextNode('偶尔发一次图的反而会留在我的关注名单,太多嘴的反而会解除关注,真不适合交太多朋友,没传统论坛好用'), emo2 = ct('img'); emo1.src = 'phpfreechat/data/public/themes/wolfbbs/smileys/jcdragon-tail-faster.gif'; emo1.alt = emo1.title = '(tail)'; emo2.src = 'phpfreechat/data/public/themes/wolfbbs/smileys/jcdragon-tea.gif'; emo2.alt = emo2.title = '(tea)'; [txt1, emo1, txt2, emo2].forEach(function(e){ span.appendChild(e); }); ChatRoomTest_base(span); }; var rootbtn = ct('button' + divid, '更多功能'), listdiv = ct('div'), functions = { '复位窗口大小': ChatRoomImpl_resizeWindow //,'添加记录:图片+文字': ChatRoomTest_addImgText //,'添加记录:文字': ChatRoomTest_addText //,'添加记录:文字+表情+文字': ChatRoomTest_addTextEmoticonText }; var funarr = []; for(var f in functions){ funarr.push({ name: f, fun: functions[f] }); } funarr.forEach(function(e){ var a = ct('a', e.name); a.onclick = function(){ e.fun(); return false; }; listdiv.appendChild(a); }); rootbtn.appendChild(listdiv); root.appendChild(rootbtn); } function ChatRoomImpl_resizeWindow( DO_NOT_ADD_PARAM ){ var windowW = window.outerWidth - window.innerWidth + ChatWindowWH[0], windowH = window.outerHeight - window.innerHeight + ChatWindowWH[1]; window.resizeTo(windowW, windowH); if(arguments.length == 0){ // 在200毫秒后重复调用一次,因为如果此前窗口是最大化的,那么边框将会无法正确计算 setTimeout(function(){ ChatRoomImpl_resizeWindow(0); }, 200); } } var ChatRoom_lastMsg = null, ChatRoom_initTime = 0; // 算了 function ChatRoomImpl_testNewMessage(){ var pfc_chat = $('.pfc_chat'); if(!pfc_chat) return; var all_msg = $$('.pfc_chat>div>div'), last_msg = $('.pfc_chat>div:last-child>div:last-child'), last_msg_sender = last_msg.querySelector('.pfc_nick>span'), // 空白即为系统消息(上线或者下线) last_msg_content = last_msg, // self_name = $('#pfc_handle').innerText, need_scroll_to_bottom = false, need_notify_message = false, need_notify_online = false, need_notify_offline = false; last_msg_sender = last_msg_sender? last_msg_sender.innerText: ''; // 将元素转换为发送者名称,null转换为空白发送者(判定为系统消息) if(ChatRoom_lastMsg == null){ var login = new Audio(); login.src = 'http://downsc.#####z.net/Files/DownLoad/sound1/201301/2554.mp3'; // WindowsXP 开机 http://sc.#####z.com/yinxiao/130108438723.htm login.volume = 0.20; login.play(); ChatRoom_lastMsg = 0; //ChatRoom_initTime = new Date().getTime(); }else{ if(ChatRoom_lastMsg != last_msg){ // 判断是否需要滚动到最底部,仅首次检测到时有效 //if(new Date().getTime() - ChatRoom_initTime < 60000) need_scroll_to_bottom = true; // 刚初始化时还没有加载图片,图片加载后会顶起来导致最新的几条消息没有显示 if(last_msg_sender == self_name) need_scroll_to_bottom = true; // 因为自己发送了新消息所以滚动到最底下 if(pfc_chat.scrollHeight - pfc_chat.scrollTop - pfc_chat.clientHeight < 250) need_scroll_to_bottom = true; // 因为之前就是滚动到最底下,但新消息的高度可能会有点问题所以这里判定为需要滚动到最底下 // 滚动到底部: if(need_scroll_to_bottom) pfc_chat.scrollTop = pfc_chat.scrollHeight; } pl('lastmsg: ' + last_msg_sender + ': ' + last_msg_content.innerText); if(ChatRoom_lastMsg == last_msg) return; ChatRoomImpl_notification({ type: 'msg', title: last_msg_sender, desc: last_msg_content }); } if(last_msg != null) ChatRoom_lastMsg = last_msg; } function ChatRoomImpl_notification(detail){ var title, raw, text, sound; switch(detail.type){ case 'msg': title = detail.title; raw = detail.desc.querySelector('.pfc_words'); // 自己发的就会多套一层span 但是别人发的不会 text = []; for(var i=0, a=raw.childNodes, len=a.length; i<len; i++){ var e = a[i]; text[text.length] = 'innerText' in e? // 如果不是文本节点 e.innerText == ''? // 如果元素节点内(A标签内)是空文本 ' [图片] ': // 用“图片”文本代替之 e.innerText: // 显示元素节点内的链接文本 e.textContent; // 显示文本节点的文本 }; text = text.join(''); sound = WAV_MSG; break; case 'online': sound = WAV_ONLINE; break; case 'offline': break; case 'err': sound = WAV_ALERT; break; default: } GM_notification({ title: title, text: text, timeout: 86400000, // 超时时间为1天 image: 'http://wolfbbs.net/favicon.ico' }); if(sound){ sound.currentTime = 0; sound.play(); } } var WAV_ONLINE = null, WAV_MSG = null, WAV_ALERT = null; function Notification_init(){ // 可以无限制调用,但必须先调用这个才能播放声音 var creating = function(url){ var player = new Audio(); player.src = url; //document.body.appendChild(player); return player; }; if(WAV_ONLINE == null) WAV_ONLINE = creating('http://downsc.#####z.net/Files/DownLoad/sound1/201308/3426.mp3'); // 狼叫 http://sc.#####z.com/yinxiao/130824455512.htm if(WAV_MSG == null) WAV_MSG = creating('http://downsc.#####z.net/Files/DownLoad/sound1/201703/8407.mp3'); // 接收消息 http://sc.#####z.com/yinxiao/170304583990.htm if(WAV_ALERT == null) WAV_ALERT = creating('http://downsc.#####z.net/Files/DownLoad/sound1/201904/11441.mp3'); // 喇叭错误 http://sc.#####z.com/yinxiao/190428481530.htm } (function(){ setInterval(onInterval, IntervalTimeout); })(); //----- my ezjs lib function fv(id){return document.getElementById(id);} function ft(tag){return document.getElementsByTagName(tag);} function fc(cname){return document.getElementsByClassName(cname);} function $(s){return document.querySelector(s);} function $$(s){return document.querySelectorAll(s);} function msgbox(msg){alert(msg);} function inputbox(title,defalt){return prompt(title,defalt);} function pl(s){console.log(s);} function ct(tag, t){ if(arguments.length > 2) pl(new Error('Somewhere might using old version to create Elements. PLEASE UPDATE YOUR CODE.')); tag = { entity: null, raw: tag, data: tag.split(/[#\.\s]/g) }; var nextStart = 0; tag.data.forEach(function(e){ nextStart ++; if(e.length == 0) return; // continue nextStart --; switch(tag.raw.charAt(nextStart)){ case ' ': case '.': addClass(tag.entity, e); break; case '#': tag.entity.id = e; break; default: tag.entity = document.createElement(e); nextStart --; } nextStart += e.length + 1; }); if(t) tag.entity.innerText = t; return tag.entity; } function hasClass(e,n){ return new RegExp("(\\s|^)"+n+"(\\s|$)").test(e.className); } function addClass(e,n){ if(!hasClass(e,n)) e.className=(e.className+' '+n).trim(); } function registerPrototype(){ Array.prototype.forEach = function(func){ for(var i=0; i<this.length; i++) try{ if(func(this[i], i, this)) return true; }catch(e){ pl(e); } }; pl('UserScript is hooked: Array.forEach'); }