狼之乐园辅助

包含功能有:聊天室辅助。

// ==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.chinaz.net/Files/DownLoad/sound1/201301/2554.mp3'; // WindowsXP 开机 http://sc.chinaz.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.chinaz.net/Files/DownLoad/sound1/201308/3426.mp3'); // 狼叫 http://sc.chinaz.com/yinxiao/130824455512.htm
    if(WAV_MSG    == null) WAV_MSG    = creating('http://downsc.chinaz.net/Files/DownLoad/sound1/201703/8407.mp3'); // 接收消息 http://sc.chinaz.com/yinxiao/170304583990.htm
    if(WAV_ALERT  == null) WAV_ALERT  = creating('http://downsc.chinaz.net/Files/DownLoad/sound1/201904/11441.mp3'); // 喇叭错误 http://sc.chinaz.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');
}