用户:StarHeartHunt/js/notification.js

来自Mooncell - 玩家共同构筑的FGO中文Wiki
跳转到导航 跳转到搜索

注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Internet Explorer:按住Ctrl的同时单击刷新,或按Ctrl-F5
  • Opera:前往菜单 → 历史(Mac为Opera → Preferences),或按Ctrl-Shift-Del,然后清除浏览数据 → 勾选“已缓存的图片和文件” → 清除数据
/*
	引入这个插件后,将在监视列表的条目发生变化以及收到各种信息时在屏幕的右下角进行通知,获取通知并不需要刷新页面。
	因为使用了Web Notification发送通知,即使网页处于收起或被遮挡等未被浏览的状态,只要保持开启,就可以收到通知。
	正常情况下:通知的左边显示产生动作的用户的头像,右边为信息,点击通知可以进入对应的条目页面。
	区别于条目变化的通知,信息的通知(如感谢,评论回复等)会持续较长的时间。
	
	在使用这个插件时,【请务必在授予权限时点击允许】,因为一些套壳浏览器(如360极速)将网页权限界面隐藏了起来,如果点了阻止,
	想要再改回来会非常麻烦,若已经点击了阻止,可以点击url栏左边的小锁标志,如果有更改通知权限的地方请更改,没有就只能选择放弃该插件了。
	插件并不能保证完全对应通知,只是起到一个辅助提醒的作用,完全准确以及条目变化的具体信息请认准监视列表页面。
	
	注意:在网络有波动时(表现为网页开启缓慢),插件可能不会正常工作。
*/

$(function(){
    if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)){
        return false
    }

    function nct(nct, url, life){
        var life = life || 8000
        if(url){ nct.onclick = function(){ open(url, '_blank') } }
        nct.addEventListener('click', function(){
            setTimeout(function(){
                nct.close()
            })
        }, 500)
        setTimeout(function(){
            nct.close()
        }, life)
    }

	function setCookie(cname,cvalue,GMTStr){
        var expires = "expires="+GMTStr;
        document.cookie = cname + "=" + cvalue + "; " + expires;
      }
      
    function getCookie(cname){
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for(var i=0; i<ca.length; i++) 
        {
            var c = ca[i].trim();
            if (c.indexOf(name)==0) return c.substring(name.length,c.length);
        }
        return "";
    }
    
    function serviceToken(flag){
    	var flag = flag || false
		var d = new Date()    	
    	if(flag){
			d.setTime(d.getTime() + (2 * 60 * 1000))
			setCookie('widget-notification-serviceOn', 'true', d.toGMTString())        		
    	}else{
    		d.setTime(d.getTime() - 1)
    		setCookie('widget-notification-serviceOn', '', d.toGMTString())
    	}
    }

	function isObjectValueEqual(a, b) {
        var aProps = Object.getOwnPropertyNames(a);
        var bProps = Object.getOwnPropertyNames(b);

        if (aProps.length != bProps.length) {
            return false;
        }

        for (var i = 0; i < aProps.length; i++) {
            var propName = aProps[i];
            if (a[propName] !== b[propName]) {
                return false;
            }
        }
        return true;
    }

    if(typeof Notification == 'undefined'){
        mw.notify('你的浏览器版本过低或不支持某些功能,无法使用Web Notification插件!', { type : 'warn' })
        setTimeout(function(){
            mw.notify('请从你的用户页移除该插件,以消除这些警告!', { type : 'warn' }); 
        }, 2000)
        return false
    }
    
    if(Notification.permission == 'default'){
        alert('点击确定关闭这条消息后,将弹出授予通知权限的窗口,届时请点击“允许”,以保证插件的正常运行。')
    }
    
    Notification.requestPermission(function(){
        var status = Notification.permission
        if(status == 'granted'){
            if(! localStorage.getItem('widget-notification-messageMark')){
                localStorage.setItem('widget-notification-messageMark', 'true')
                nct(new Notification('欢迎', {
                    body : '您已经成功开启通知功能,欢迎使用!',
                    icon : 'https://fgo.wiki/resources/assets/wiki.png'
                }))
            }
        }
        if(status == 'denied'){
            alert('您未能正确授予通知权限,若要继续使用,请在url栏左侧点击小锁标志开启权限,若无法设置,请从你的用户页删去此插件。')
        }
    })

	var api = new mw.Api()

    var userName = ''
    api.get({
        "action": "query",
        "format": "json",
        "meta": "userinfo",
        "utf8": 1
    }).done(function(data){
        userName = data.query.userinfo.name
    })

    var pages = []
    var pagesStr = ''
    var apiContinue = null
    function getWatch(){
        var request = {
            "action": "query",
            "format": "json",
            "list": "watchlistraw",
            "utf8": 1,
            "wrlimit": "max"
        }
        if(apiContinue){
            request.wrcontinue = apiContinue.wrcontinue
            request.continue = apiContinue.continue
        }else{
            delete request.wrcontinue
            delete request.continue
        }
        api.get(request).done(function(data){
            apiContinue = data.continue
            var watch = data.watchlistraw
            $.each(watch, function(){
                if(this.title != 'User talk:' + userName){
                    pages.push(this.title)
                }
            })
            pagesStr = pages.join('|')
            localStorage.setItem('widget-notification-watchList', pagesStr)
            apiContinue && getWatch()
        })
    }

    if(location.href == encodeURIComponent('https://fgo.wiki/w/Special:编辑监视列表')){
    	if(/已从您的监视列表移除%d{1,2}个标题/.test($('#mw-content-text').text())){
    		pages = []
    		getWatch()
    	}
    }

	function setWatch(act){
		var act = act || 'push'
		var watch = localStorage.getItem('widget-notification-watchList').split('|')
		var thisTitle = location.href.replace(/https\:\/\/fgo\.wiki\/w\/(.+)$/, '$1')
		if(/(index\.php|action=edit)/.test(thisTitle)){
			thisTitle = thisTitle.replace(/.*title=(.+?)(&.+|$)/, '$1')
		}
		thisTitle = decodeURIComponent(thisTitle)
		var talkTitle  = 'Talk:' + thisTitle
		var npRE = /(Template|Mooncell|模块|User|Help):(.+)/
		if(npRE.test(thisTitle)){
			var prefix = thisTitle.replace(npRE, '$1')
			var name = thisTitle.replace(npRE, '$2')
		}
		if(/(Template|Mooncell|User|Help)/.test(prefix)){
			talkTitle = prefix + ' talk:' + name 
		}
		if(prefix == '模块'){
			talkTitle = '模块讨论:' + name
		}
		if(act == 'push'){
			var flag = true
			$.each(watch, function(){
				if(this == thisTitle || this == talkTitle){ flag = false }
			})
			flag && watch.push(thisTitle, talkTitle)
		}
		(act == 'remove') && $.each(watch, function(i){
			if(this == thisTitle || this == talkTitle){
				watch[i] = ''
			}
		})
		
		var watchStr = watch.join('|')
		localStorage.setItem('widget-notification-watchList', watchStr)
	}

    $('#ca-watch, #ca-unwatch').mousedown(function(){
    	(this.id.toLowerCase() == 'ca-watch') && setWatch()
    	;(this.id.toLowerCase() == 'ca-unwatch') && setWatch('remove')
    })
    $('#mw-editpage-watch').mouseup(function(){
    	$('#wpWatchthis').is(':checked') ? setWatch('remove') : setWatch()   // 此处获得了false为选中,true为未选中
    })
    
    getCookie('widget-notification-serviceOn') != 'true' && main()
    var watchdogKey = setInterval(function(){
        if(getCookie('widget-notification-serviceOn') != 'true'){
            main()
            clearInterval(watchdogKey)
        }
    }, 5000 + Math.random())


	function main(){
		console.log('notification.js已开启服务,开始进行消息与监视列表的通知推送。')
		serviceToken(true)
		;(function serviceStart(){
			setTimeout(function(){
				serviceToken(true)
				serviceStart()
			}, 60 * 1000)
		})()
		
		$(window).on('unload', function(){
			serviceToken()
		})
	
        if(! getCookie('widget-notification-removeTime')){
            var d = new Date()
            d.setTime(d.getTime()+(24*60*60*1000))
            setCookie('widget-notification-removeTime', d.toGMTString(), d.toGMTString())
        }
        
        ;(function(){
            function getTimeISO8061(){
                var fun = function(num){
                    return num < 10 ? '0' + num : num
                }
                var time = new Date()
                var y = time.getUTCFullYear(),
                m = fun(time.getUTCMonth() + 1),
                d = fun(time.getUTCDate()),
                h = fun(time.getUTCHours()),
                i = fun(time.getUTCMinutes()),
                s = fun(time.getUTCSeconds())
                var timeStr = y + '-' + m + '-' + d + 'T' + h + ':' + i + ':' + s + 'Z'
                return timeStr
            }
            
            setTimeout(function(){
            	getWatch()
            	console.log('notification.js:已获取监视列表')
            }, 30000)
            
            var lastSearchTime = getTimeISO8061()
            setInterval(function(){
                var usedStr = getCookie('widget-notification-usedIds')
                if(usedStr){
                    var used = usedStr.split('|')
                }else{
                    var used = []
                }		
                
                var rcidsStr = getCookie('widget-notification-rcids')
                if(rcidsStr){
                	var rcids = rcidsStr.split('|')
                }else{
                	var rcids = []
                }
                
                var memoryTime = getCookie('widget-notification-lastSearchTime')
                if(memoryTime){
                    lastSearchTime = memoryTime
                }
                api.get({
                    action: 'query',
                    format: 'json',
                    prop: '',
                    list: 'recentchanges',
                    rcend : lastSearchTime,
                    rclimit: 'max',
                    utf8: 1,
                    formatversion: 1,
                    rctoponly: 1,
                    rcexcludeuser: userName,
                    rcprop: 'title|user|comment|ids'
                }).done(function(data){
                    var d = new Date()
                    d.setTime(d.getTime()+(30*60*1000))	    	
                    setCookie('widget-notification-lastSearchTime', getTimeISO8061(), d.toGMTString())
                    var query = data.query.recentchanges
                    var filter = []
                    var pages = localStorage.getItem('widget-notification-watchList').split('|')
                    $.each(query, function(){
                        for(var i=0; i < pages.length; i++){
                            if(this.title == pages[i]){
                            	var flag = true
                            	for(var j=0; j < filter.length; j++){
                            		if(isObjectValueEqual(this, filter[j])){
                            			flag = false
                            		}
                            	}
                                flag && filter.push(this)
                            }
                        }
                    })
					
                    $.each(filter, function(){
	                    var mark = true
	                    for(var i=0; i < rcids.length; i++){
	                        if(this.rcids == rcids[i]){
	                            mark = false
	                        }
	                    }	          
	                    
                    	if(mark){
	                    	var commentStr = '在【' + this.title + '】作出了编辑。'
	                        if(this.comment){
	                            this.comment = this.comment.replace('// Edit via Wikiplus', '')
	                            var chapterRE = /\/\* (.+?) \*\/([^\/]*)/
	                            if(chapterRE.test(this.comment)){
	                                var chapter = this.comment.replace(chapterRE, '$1')
	                                var comment = this.comment.replace(chapterRE, '$2').replace(/[\s]*(.+)[\s]*/, '$1')
	                                if(chapter && (chapter != this.comment)){
	                                    commentStr += '\n章节 → ' +  chapter
	                                }
	                                if(comment && (comment != this.comment)){
	                                    commentStr += '\n摘要:' + comment
	                                }	        			
	                            }else{
	                                commentStr += '\n摘要:' + this.comment.replace(/[\s]*(.+)[\s]*/, '$1')
	                            }
	        
	                        }
	                        nct(new Notification(this.user, {
	                            body : commentStr,
	                            icon : '//fgo.wiki/extensions/Avatar/avatar.php?user=' + this.user
	                        }), 'https://fgo.wiki/index.php?title=' + this.title + '&curid=' + this.pageid + '&diff=' + this.revid + '&oldid=' + this.old_revid)    
	                        console.log('编辑者:' + this.user + '\n' + commentStr)
                    		rcids.push(this.rcid)
                    	}
	                    var rcidsStr = rcids.join('|')
	                    setCookie('widget-notification-rcids', rcidsStr, getCookie('widget-notification-removeTime'))                        
                    })
                })
        
                api.get({
                    action: "query",
                    format: "json",
                    prop: "",
                    meta: "notifications",
                    utf8: 1,
                    notfilter: "!read",
                    notlimit: "max"
                }).done(function(data){
                    var query = data.query.notifications.list
                    $.each(query, function(){
                        var msgNct = function(text, $this){
                            nct(new Notification($this.agent.name + ':', {
                                body : text,
                                icon :'//fgo.wiki/extensions/Avatar/avatar.php?user=' + $this.agent.name,
                                sticky : true
                            }), 'https://fgo.wiki/' + $this.title.full, 9999999999)
                            console.log($this.agent.name + ':' + text)
                        }
                        
                        var mark = true
                        for(var i=0; i < used.length; i++){
                            if(this.id == used[i]){
                                mark = false
                            }
                        }
                        
                        if(mark){
                            switch(this.type){
                                case 'edit-thank' : {
                                    msgNct('对您在【' + this.title.full + '】的编辑表示了感谢!', this)
                                    break
                                }
                                case 'edit-user-talk' : {
                                    msgNct('在您的讨论页留言了。', this)
                                    break
                                }
                                case 'flowthread_reply' : {
                                    msgNct('回复了您在【' + this.title.full + '】的评论。', this)
                                    break
                                }
                                case 'flowthread_userpage' : {
                                    msgNct('在您的用户页留下了评论。', this)
                                    break
                                }
                                case 'flowthread_delete' : {
                                	nct(new Notification('通知', {
		                                body : '您在【' + this.title.full + '】下的评论被删除了。',
		                                icon : 'https://fgo.wiki/resources/assets/wiki.png',
		                                sticky : true
		                            }), 'https://fgo.wiki/' + this.title.full, 9999999999)
		                            console.log('评论被删除:在页面【' + this.title.full + '】上')
                                    break
                                }
                                case 'mention' : {
                                	msgNct('在【' + this.title.full + '】中提到了您。', this)
                                	break
                                }
                            }
                            used.push(this.id)
                        }
                    })
                    var usedStr = used.join('|')
                    setCookie('widget-notification-usedIds', usedStr, getCookie('widget-notification-removeTime'))			
                })
        
            }, 60000)
            
        })()
	
	}    
})