// ==UserScript== // @name 魔灯 // @namespace Scripts // @match *://test2.adsdesk.cn/* // @match *://pre.adsdesk.cn/* // @grant none // @version 1.0 // @author mobvista // @require https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js // @require https://cdn.jsdelivr.net/npm/watermark-package@2.1.2/dist/watermark.umd.min.js // @description 2023/12/6 13:42:17 // ==/UserScript== // noinspection JSUnresolvedReference // 开启调试模式, 此模式会输出详尽日志 const debugMode = 1 const DOMAIN = '.adsdesk.cn' const K_UC = 'uniqueCode' const K_USER = 'user' const K_TOKEN = 'token' // 定时器ID let watermarkTimerId let logoutTimerId (function () { 'use strict'; function start() { L('start 执行开始') let authToken = getQueryVariable('authToken'); // 新登录 if (authToken) { L('匹配到authToken, 准备执行登录. decodeAuthToken: ' + decodeURIComponent(authToken)) const authData = JSON.parse(decodeURIComponent(authToken)) Cookies.set(CN(K_UC), authData[K_UC]) Cookies.set(CN(K_USER), authData[K_USER]) modeng(authData[K_TOKEN], authData[K_UC]) } // 已登录 if (Cookies.get(CN(K_USER))) { L('匹配到模登用户, 准备添加监控. K_USER: ' + Cookies.get(CN(K_USER))) watermarkTimerId = setInterval( addWatermark, 1000) // 添加登出监控 logoutTimerId = setInterval(addLogoutListener, 500) // 添加请求监控 setTimeout(addRequestListener, 10) } L('start 执行完成') } start() })() function modeng(token, code) { // 如果未设置标识, 则先 const flag = Cookies.get(code) if (flag === undefined) { Cookies.set(code, '1') } if (Cookies.get(code) === '1') { Cookies.set(K_TOKEN, token) Cookies.set(K_TOKEN, token, {domain: DOMAIN}) // 1分钟后删除 Cookies.set(code, '0', { expires: new Date(new Date().getTime() + 60000) }) location.reload(); } } // 添加水印 function addWatermark() { watermark.setWaterMark( { w_texts: ['模拟环境[' + Cookies.get(CN(K_USER)) + ']' + getNowTime()], w_options: { w_width: 310, w_height: 160, w_opacity: '0.21' } } ) // 第一次时候会报错 try { watermark.removeWatermark() } catch (e) { } } // 登录监控 function addLogoutListener() { // L('登出监控. K_TOKEN: ' + Cookies.get(K_TOKEN)) // 登录凭证不存在了, 只有2种可能, 1:登录; 2:清理缓存, 此时需要把modeng信息清理掉 if (!Cookies.get(K_TOKEN)) { L('登录操作, 清理modeng') Cookies.remove(CN(K_UC)) Cookies.remove(CN(K_USER)) watermark.removeWatermark() clearInterval(watermarkTimerId) clearInterval(logoutTimerId) } } const REQ_LOG_SAVE_URL = '/openapi/request_log/save' // 请求监控 function addRequestListener() { L('添加请求监控') window.addEventListener('ajaxReadyStateChange', function (e) { const xhr = e.detail; // 不放在onload里会执行多次3次(状态变更事件会触发3次) xhr.onload = function () { L('触发请求监控') console.log('Request URL:', xhr.orignUrl); // console.log('Request Body:', xhr.body); // console.log('Response Body:', xhr.response); const optUser = Cookies.get(CN(K_USER)) L("optUser: " + optUser) if (optUser) { const requestUrl = validUrl(xhr.orignUrl); L("requestUrl: " + requestUrl) if (requestUrl) { const param = { optUser, 'uniqueCode': Cookies.get(CN(K_UC)), "authToken": '', requestUrl, "requestMethod": xhr.method, "requestBody": xhr.body, "responseCode": xhr.status, "responseBody": xhr.response } sendLog(param) } } } }) } // 发送日志 function sendLog(param) { const httpRequest = new XMLHttpRequest(); // const sendLogUrl = (debugMode ? 'http://localhost/dev-api' : 'https://oms-api.adsdesk.cn') + REQ_LOG_SAVE_URL // 临时能调用的地址 const sendLogUrl = 'https://oms-api.adsdesk.cn/openapi/adsdesk/request_log/save' httpRequest.open('POST', sendLogUrl, true); httpRequest.setRequestHeader("Content-type", "application/json"); httpRequest.send(JSON.stringify(param)); } //region 工具函数封装 function getQueryVariable(variable) { const query = window.location.search.substring(1) const vars = query.split("&") for (let i = 0; i < vars.length; i++) { const pair = vars[i].split("=") if (pair[0] === variable) { return pair[1] } } return false } function validUrl(fullUrl) { if (!fullUrl || fullUrl.indexOf(REQ_LOG_SAVE_URL) >= 0) { return false } if (fullUrl.indexOf('adsdesk.cn/adsdesk') < 0) { return false } fullUrl = fullUrl.replace("//", "$") return fullUrl.substring(fullUrl.indexOf("/") + 1); } function getNowTime() { const now = new Date(); let year = now.getFullYear(), month = now.getMonth() + 1, date = now.getDate(), h = now.getHours(), m = now.getMinutes(), s = now.getSeconds() month = month < 10 ? '0' + month : month date = date < 10 ? '0' + date : date h = h < 10 ? '0' + h : h m = m < 10 ? '0' + m : m s = s < 10 ? '0' + s : s // return year + "-" + month + "-" + date + " " + h + ":" + m; return h + ":" + m + ":" + s; } function CN(name) { return 'modeng_' + name } function L(msg) { if (debugMode) { console.log(msg) } } //endregion //region 监听HTTP请求 //(function(){})(): 表示立即执行, 注册全局代理对象(防止重写XMLHttpRequest时循环调用?) ;(function () { if (typeof window.CustomEvent === "function") return false; function CustomEvent(event, params) { params = params || {bubbles: false, cancelable: false, detail: undefined}; const evt = document.createEvent('CustomEvent'); evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); return evt; } CustomEvent.prototype = window.Event.prototype; window.CustomEvent = CustomEvent; })(); // 重写 XMLHttpRequest 全局拦截器 ;(function () { function ajaxEventTrigger(event) { const ajaxEvent = new CustomEvent(event, {detail: this}); window.dispatchEvent(ajaxEvent); } const oldXHR = window.XMLHttpRequest; function newXHR() { const realXHR = new oldXHR(); // abort, error, load, loadstart, progress, timeout, loadend 可按需重写, 此处仅需要readystatechange realXHR.addEventListener('readystatechange', function () { ajaxEventTrigger.call(this, 'ajaxReadyStateChange'); }, false); let send = realXHR.send; realXHR.send = function (...arg) { send.apply(realXHR, arg); realXHR.body = arg[0]; ajaxEventTrigger.call(realXHR, 'ajaxSend'); } let open = realXHR.open; realXHR.open = function (...arg) { open.apply(realXHR, arg) realXHR.method = arg[0]; realXHR.orignUrl = arg[1]; realXHR.async = arg[2]; ajaxEventTrigger.call(realXHR, 'ajaxOpen'); } let setRequestHeader = realXHR.setRequestHeader; realXHR.requestHeader = {}; realXHR.setRequestHeader = function (name, value) { realXHR.requestHeader[name] = value; setRequestHeader.call(realXHR, name, value) } return realXHR; } window.XMLHttpRequest = newXHR; })(); //endregion