网址监控通知

监控是否跳转到特殊网址,如果跳转则通过webhook(飞书等)通知,可用于登录(不可用)失效识别等

// ==UserScript==
// @name         网址监控通知
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  监控是否跳转到特殊网址,如果跳转则通过webhook(飞书等)通知,可用于登录(不可用)失效识别等
// @author       Austin.Young
// @match        *
// @include      *
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// @grant        GM_xmlhttpRequest
// @grant        GM.xmlHttpRequest
// @connect      self
// @connect      localhost
// @connect      www.feishu.cn
// @connect      open.feishu.cn
// @connect      *  // 不支持 * 匹配
// @grant        GM_getValue
// @grant        GM_setValue
// @license MIT

// ==/UserScript==
let webHookList = []
let matchUrlList = []
let historyList = []
let detectDelay = 1000;
const preIdName = 'austinConfig_';

(function () {
    'use strict';
    const menu_command_id_1 = GM_registerMenuCommand("设置通知规则", function (event) {
        addConfig()
    }, {
        accessKey: "U",
        autoClose: true
    });
    // 进行检测
    getPara();
    setTimeout(detectUrl,detectDelay);
})();
var newElement =null,myshadowRoot =null;
function getDom(){
    let dom;
    if(myshadowRoot!=null){
        dom = myshadowRoot
    }else{
        dom = document
    }
    return dom
}
// 所有对象增加 austinConfig 前缀
function $(id,ignore) {
    let domId = preIdName + id
    let obj = getDom().getElementById(domId)
    if (obj == null) {
        if(ignore)
        {
            // 忽略提醒,返回失败
            return false
        }
        alert('找不到DOM对象:' + domId)
        return {};
    }
    return obj
}
function addConfig() {
    if(newElement!=null)return;// 已经创建过
    newElement = document.createElement("div");
    // 创建 Shadow Root
    // myshadowRoot = document.body.attachShadow({ mode: 'open' });
    // myshadowRoot.appendChild(newElement)
    // newElement.style.color="black"
    // newElement.style.textAlign="left"
    // newElement.style.padding="2px"
    // newElement.style.margin="0px"
    newElement.style.all ="initial"
    myshadowRoot = newElement.attachShadow({ mode: 'closed' });
    myshadowRoot.innerHTML = `
    <div style="inset: 10% auto auto 20%; border: 1px solid black;
    margin: 0px; max-height: 95%; opacity: 1;
    position: fixed; display: block;
    min-width: 320px;width:60%;max-width: 95%;
    z-index: 2147483647; overflow: auto; padding: 0px;" id="Panel">
        <div
            style="padding: 2px; background-color: rgb(49, 49, 49);color: #c7ed1c;font-weight: bold;font-size:large; text-align: center;line-height: 26px;">
            参数设置
            <div style="float:right;">
                <span style="border:1px white solid;cursor: pointer;" id="Refresh" title="刷新">🔁</span>
                <span style="border:1px white solid;cursor: pointer;" id="Save" title="保存">✔️</span>
                <span id="Close"
                    style="border:1px white solid;cursor: pointer;" title="关闭">❌</span>
            </div>
        </div>
        <div style="overflow: auto;height: 600px;width: 100%;background-color:aliceblue;">
            <div style="padding: 2px;">
                <div style="font-weight: bold;">页面载入后延时<input type="number" id="btDelay" min="0" step="1000" placeholder="毫秒数" style="width:5em"/>毫秒检测</div>
                <hr/>
                <div style="font-weight: bold;">通知参数 <input type="button" id="btNoticeList" value="显示/修改通知JSON"></div>
                举例 [{"name":"飞书","url":"https://open.feishu.cn/open-apis/bot/xxx","text":"[过期]url:{Url},监控:{MatchName}"}]
                <table border="1" style="border-collapse: collapse;">
                    <thead style="background-color:rgb(49, 49, 49);font-weight: bold;color: white;">
                        <td>序号</td>
                        <td>通知名称(name,不能重复)</td>
                        <td>Webhook地址(url)</td>
                        <td>发送内容(text,发送的内容,{MatchName}=监控名称,{Url}=当时访问的网址)</td>
                        <td>操作</td>
                    </thead>
                    <tbody id="NoticeShow"></tbody>
                </table>
                <textarea id="txtNoticeList" style="display: none;" cols="100" rows="7"
                    placeholder='JSON格式数组,如下:&#10;[{"name":"飞书","url":"https://www.feishu.cn/xxx","text":"机器人"}]'></textarea>
                <hr />
                <div style="font-weight: bold;">网址监控参数 <input type="button" id="btMatchList" value="显示/修改监控JSON"></div>
                举例 [{"name":"京东","type":0,"strMatch":"passport.jd.com/new/login.aspx","notice":"飞书"}]
                <table border="1" style="border-collapse: collapse;">
                    <thead style="background-color:rgb(49, 49, 49);font-weight: bold;color: white;">
                        <td>序号</td>
                        <td>监控名称(name)</td>
                        <td>处理类型(type,0=包含,1=正则)</td>
                        <td>匹配内容(strMatch,字符串或正则表达式)</td>
                        <td>通知名(notice,对应的通知名称)</td>
                    </thead>
                    <tbody id="UrlMatch"></tbody>
                </table>
                <textarea id="txtMatchUrlList" style="display: none;" cols="100" rows="7"
                    placeholder='JSON格式数组,类型参数 0 为字符串包含匹配,1为正则匹配。如下:&#10;[{"name":"名称","type":0,"strMatch":"https://www.feishu.cn/xxx","notice":""}]'></textarea>
                <hr />
                <div style="font-weight: bold;">最近消息列表(保留最近50次) <input type="button" id="btClear" value="清空历史"><br/></div>
                <textarea id="txtLastMsg" cols="100" rows="9" readonly="readonly"></textarea>
            </div>
        </div>
    </div>
`.replace(/id="(.*?)"/g, 'id="' + preIdName + '$1"')  // 全部临时替换为
    document.body.appendChild(newElement);
    $('Save').onclick = function () {
        if(saveConfig()){
            closeIt()
        }
    }
    $('Close').onclick = closeIt
    $('Refresh').onclick = function () {
        closeIt()
        addConfig()
        return;
    }
    getPara()
    showNoticeList(webHookList)
    showMatchList(matchUrlList)
    $('btDelay').value = detectDelay
    $('txtLastMsg').value = JSON.stringify(historyList, null, 2)
    $('btClear').onclick = function(){
        if(confirm('确认清空历史记录?'))
        {
            historyList = []
            GM_setValue(preIdName + 'History', historyList);
            $('txtLastMsg').value = JSON.stringify(historyList, null, 2)
        }
    }
    $('btNoticeList').onclick = NoticeList
    $('btMatchList').onclick = MatchList
}
function closeIt() {
    document.body.removeChild(newElement);
    newElement = null
    myshadowRoot =null
}
function NoticeList() {
    checkList('txtNoticeList', webHookList, showNoticeList)
}
function MatchList() {
    checkList('txtMatchUrlList', matchUrlList, showMatchList)
}
function checkList(objId, objList, fun) {
    let list = $(objId)
    if (list.style.display == 'none') {
        list.style.display = ''
        if (objList.length > 0) {
            list.value = JSON.stringify(objList,null,2)
        } else {
            list.value = ''
        }
    } else {
        list.style.display = 'none'
        // 显示为列表
        fun(list.value)
    }
}
// 检查入参是否是合法json,并修改入参数组
function checkJson(val) {
    let arr = []
    if (typeof val == 'string') {
        if (val.trim() == '') {
            val = '[]'
        }
        try {
            arr = JSON.parse(val)
        } catch (e) {
            alert('转换通知JSON异常!\n' + e)
            return { arr, res: false }
        }
    } else {
        arr = val
    }
    return { arr, res: true }
}
function showNoticeList(val) {
    let obj = checkJson(val)
    if (!obj.res) return
    webHookList = obj.arr
    let bt = `${preIdName}BtTest`
    let html = webHookList.map((x, i) => {
        return `<tr><td>${i+1}</td><td>${x.name}</td><td>${x.url}</td><td>${x.text}</td><td><input type="button" class="${bt}" value="测试"></td></tr>`
    }).join('')
    $('NoticeShow').innerHTML = html
    // 绑定事件
    getDom().querySelectorAll('.'+bt).forEach((x,i)=>{
        x.onclick = function(){sendWebHook(i)}
    })
}
function showMatchList(val) {
    let obj = checkJson(val)
    if (!obj.res) return
    matchUrlList = obj.arr
    let html = matchUrlList.map((x, i) => {
        // 如果 x.notice 在 webHookList 中找不到则要背景提示
        let findNotice = webHookList.some(y => {
            return y.name == x.notice
        })
        return `<tr style="color:${findNotice ? '' : 'red'}"><td>${i+1}</td><td>${x.name}</td><td>${x.type ? '正则' : '包含'}</td><td>${x.strMatch}</td><td>${x.notice} </td></tr>`
    }).join('')
    $('UrlMatch').innerHTML = html
}
function getPara() {
    webHookList = GM_getValue(preIdName + 'Notice', []);
    matchUrlList = GM_getValue(preIdName + 'Match', []);
    historyList = GM_getValue(preIdName + 'History', []);
    detectDelay = GM_getValue(preIdName + 'Delay', detectDelay ); // 延时检测毫秒数
}
function saveConfig() {
    if($('txtNoticeList').style.display!='none'){
        alert('通知参数未保存,请点击 显示/修改 按钮')
        return false
    }
    if($('txtMatchUrlList').style.display!='none'){
        alert('网址监控参数未保存,请点击 显示/修改 按钮')
        return false
    }
    let tmpDelay = parseInt($('btDelay').value)
    detectDelay = isNaN(tmpDelay)?detectDelay:tmpDelay
    GM_setValue(preIdName + 'Notice', webHookList);
    GM_setValue(preIdName + 'Match', matchUrlList);
    GM_setValue(preIdName + 'Delay', detectDelay);
    console.log('saveConfig')
    return true
}
function sendWebHook(i) {
    let obj = webHookList[i]
    if(obj!=null)sendWebHookCore(obj)
}
// matchName 为null 表示是手工测试触发,不是自动任务触发
async function sendWebHookCore(obj,matchName,triggerUrl) {
    let content = obj.text
    if(matchName){
        content = content.replace(/{MatchName}/ig,matchName)
    }
    if(triggerUrl){
        content = content.replace(/{Url}/ig,triggerUrl)
    }
    let r ;
    try {
        r = await GM.xmlHttpRequest(
            {
                method: "GET",
                url: obj.url,
                headers: {
                    "Content-Type": "application/json"
                },
                data: JSON.stringify({ "msg_type": "text", "content": { "text": content } }),
                method: "POST"
            }
        );
    } catch (e) {
        console.error(e)
        alert('发送异常:'+e.error)
        return
    }

    let rt = r.responseText
    let res = {},resTxt=''
    try{
        res = JSON.parse(rt);
    }catch (e)
    {
        resTxt = ('返回值:['+rt+']解析异常:'+ e )
    }
    if(res.code==0)
    {
        resTxt = ('成功发送')
    }else{
        resTxt = ('发送失败:['+JSON.stringify(res)+']')
    }
    if(matchName!=null){
        let histObj = {
            '监控名称':matchName,
            '通知名称':obj.name,
            '时间':new Date().toLocaleString(),
            '消息内容':content,
            '发送结果': resTxt
        }
        historyList.unshift(histObj)
        if(historyList.length>50)
        {
            historyList.pop()
        }
        GM_setValue(preIdName + 'History', historyList);
    }else{
        alert(resTxt)
    }
}
function detectUrl()
{
    let url = location.href
    matchUrlList.forEach(x=>{
        // 地址匹配
        let matched = false
        if(x.type)
        {
            // 正则
            matched = new RegExp(x.strMatch,ig).exec(url)
        }else{
            // 包含
            matched = url.indexOf(x.strMatch)>-1
        }
        // 找通知方式,如果通知方式
        let findNotice = webHookList.find(y => y.name == x.notice)
        if(matched && findNotice)
        {
            sendWebHookCore(findNotice,x.name,url)
        }
    })
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址