MonkeyModifier

Change webpage content

  1. // ==UserScript==
  2. // @name MonkeyModifier
  3. // @namespace https://github.com/JiyuShao/greasyfork-scripts
  4. // @version 2024-08-21
  5. // @description Change webpage content
  6. // @author Jiyu Shao <jiyu.shao@gmail.com>
  7. // @license MIT
  8. // @match *://*/*
  9. // @run-at document-start
  10. // @grant unsafeWindow
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. // ################### common tools
  16. function querySelectorAllWithCurrentNode(node, querySelector) {
  17. let result = [];
  18. if (node.matches(querySelector)) {
  19. result.push(node);
  20. }
  21. result = [...result, ...node.querySelectorAll(querySelector)];
  22. return result;
  23. }
  24.  
  25. function formatTimestamp(timestamp) {
  26. // 创建 Date 对象
  27. const date = new Date(timestamp);
  28.  
  29. // 获取年、月、日、小时、分钟、秒
  30. const year = date.getFullYear();
  31. const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,所以需要 +1
  32. const day = String(date.getDate()).padStart(2, '0');
  33. const hours = String(date.getHours()).padStart(2, '0');
  34. const minutes = String(date.getMinutes()).padStart(2, '0');
  35. const seconds = String(date.getSeconds()).padStart(2, '0');
  36.  
  37. // 拼接日期和时间
  38. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  39. }
  40. function replaceTextInNode(node, originalText, replaceText) {
  41. // 如果当前节点是文本节点并且包含 originalText
  42. if (node instanceof Text && node.textContent.includes(originalText)) {
  43. // 替换文本
  44. node.textContent = node.textContent.replace(originalText, replaceText);
  45. }
  46.  
  47. // 如果当前节点有子节点,递归处理每个子节点
  48. if (node.hasChildNodes()) {
  49. node.childNodes.forEach((child) => {
  50. replaceTextInNode(child, originalText, replaceText);
  51. });
  52. }
  53. }
  54.  
  55. function registerMutationObserver(node, config = {}, options = {}) {
  56. const finalConfig = {
  57. attributes: false,
  58. childList: true,
  59. subtree: true,
  60. ...config,
  61. };
  62.  
  63. const finalOptions = {
  64. // 元素的属性发生了变化
  65. attributes: options.attributes || [],
  66. // 子节点列表发生了变化
  67. childList: {
  68. addedNodes:
  69. options.childList.addedNodes ||
  70. [
  71. // {
  72. // filter: (node) => {},
  73. // action: (node) => {},
  74. // }
  75. ],
  76. removedNodes: options.childList.removedNodes || [],
  77. },
  78. // 文本节点的内容发生了变化
  79. characterData: options.characterData || [],
  80. };
  81.  
  82. const observer = new MutationObserver((mutationsList, _observer) => {
  83. mutationsList.forEach((mutation) => {
  84. if (mutation.type === 'attributes') {
  85. finalOptions.attributes.forEach(({ filter, action }) => {
  86. try {
  87. if (filter(mutation.target, mutation)) {
  88. action(mutation.target, mutation);
  89. }
  90. } catch (error) {
  91. console.error(
  92. 'MutationObserver attributes callback failed:',
  93. mutation.target,
  94. error
  95. );
  96. }
  97. });
  98. }
  99. if (mutation.type === 'childList') {
  100. // 检查是否有新增的元素
  101. mutation.addedNodes.forEach((node) => {
  102. finalOptions.childList.addedNodes.forEach(({ filter, action }) => {
  103. try {
  104. if (
  105. [Node.TEXT_NODE, Node.COMMENT_NODE].includes(node.nodeType)
  106. ) {
  107. return;
  108. }
  109. if (filter(node, mutation)) {
  110. action(node, mutation);
  111. }
  112. } catch (error) {
  113. console.error(
  114. 'MutationObserver childList.addedNodes callback failed:',
  115. node,
  116. error
  117. );
  118. }
  119. });
  120. });
  121.  
  122. // 检查是否有删除元素
  123. mutation.removedNodes.forEach((node) => {
  124. finalOptions.childList.removedNodes.forEach((filter, action) => {
  125. try {
  126. if (
  127. [Node.TEXT_NODE, Node.COMMENT_NODE].includes(node.nodeType)
  128. ) {
  129. return;
  130. }
  131. if (filter(node, mutation)) {
  132. action(node, mutation);
  133. }
  134. } catch (error) {
  135. console.error(
  136. 'MutationObserver childList.removedNodes callback failed:',
  137. node,
  138. error
  139. );
  140. }
  141. });
  142. });
  143. }
  144. if (mutation.type === 'characterData') {
  145. finalOptions.characterData.forEach(({ filter, action }) => {
  146. try {
  147. if (filter(mutation.target, mutation)) {
  148. action(mutation.target, mutation);
  149. }
  150. } catch (error) {
  151. console.error(
  152. 'MutationObserver characterData callback failed:',
  153. mutation.target,
  154. error
  155. );
  156. }
  157. });
  158. }
  159. });
  160. });
  161. observer.observe(node, finalConfig);
  162. return observer;
  163. }
  164.  
  165. function registerFetchModifier(modifierList) {
  166. const originalFetch = unsafeWindow.fetch;
  167. unsafeWindow.fetch = function (url, options) {
  168. let finalUrl = url;
  169. let finalOptions = { ...options };
  170. let finalResult = null;
  171. const matchedModifierList = modifierList.filter((e) =>
  172. e.test(finalUrl, finalOptions)
  173. );
  174. for (const currentModifier of matchedModifierList) {
  175. if (currentModifier.prerequest) {
  176. [finalUrl, finalOptions] = currentModifier.prerequest(
  177. finalUrl,
  178. finalOptions
  179. );
  180. }
  181. }
  182. finalResult = originalFetch(finalUrl, finalOptions);
  183. for (const currentModifier of matchedModifierList) {
  184. if (currentModifier.preresponse) {
  185. finalResult = currentModifier.preresponse(finalResult);
  186. }
  187. }
  188. return finalResult;
  189. };
  190. }
  191.  
  192. function registerXMLHttpRequestPolyfill() {
  193. // 保存原始的 XMLHttpRequest 构造函数
  194. const originalXMLHttpRequest = unsafeWindow.XMLHttpRequest;
  195.  
  196. // 定义新的 XMLHttpRequest 构造函数
  197. unsafeWindow.XMLHttpRequest = class extends originalXMLHttpRequest {
  198. constructor() {
  199. super();
  200. this._responseType = ''; // 存储 responseType
  201. this._onreadystatechange = null; // 存储 onreadystatechange 函数
  202. this._onload = null; // 存储 onload 函数
  203. this._onloadend = null; // 存储 onloadend 函数
  204. this._sendData = null; // 存储 send 方法的数据
  205. this._headers = {}; // 存储请求头
  206. this._method = null; // 存储请求方法
  207. this._url = null; // 存储请求 URL
  208. this._async = true; // 存储异步标志
  209. this._user = null; // 存储用户名
  210. this._password = null; // 存储密码
  211. this._readyState = XMLHttpRequest.UNSENT; // 存储 readyState
  212. this._status = 0; // 存储状态码
  213. this._statusText = ''; // 存储状态文本
  214. this._response = null; // 存储响应对象
  215. this._responseText = ''; // 存储响应文本
  216. this._responseURL = ''; // 存储响应 URL
  217. this._responseHeaders = null; // 存储响应头
  218. }
  219.  
  220. get open() {
  221. return this._open;
  222. }
  223. set open(value) {}
  224.  
  225. _open(method, url, async = true, user = null, password = null) {
  226. this._method = method;
  227. this._url = url;
  228. this._async = async;
  229. this._user = user;
  230. this._password = password;
  231. this._readyState = XMLHttpRequest.OPENED;
  232. }
  233.  
  234. get send() {
  235. return this._send;
  236. }
  237. set send(value) {}
  238. _send(data) {
  239. this._sendData = data;
  240. this._sendRequest();
  241. }
  242.  
  243. _sendRequest() {
  244. const self = this;
  245.  
  246. // 根据 responseType 设置 fetch 的返回类型
  247. const fetchOptions = {
  248. method: this._method,
  249. headers: new Headers(this._headers),
  250. credentials: this.withCredentials ? 'include' : 'same-origin',
  251. body: this._sendData || undefined,
  252. };
  253.  
  254. // 发送 fetch 请求
  255. return unsafeWindow
  256. .fetch(this._url, fetchOptions)
  257. .then((response) => {
  258. self._response = response;
  259. self._status = response.status;
  260. self._statusText = response.statusText;
  261. self._responseHeaders = response.headers;
  262. self._readyState = XMLHttpRequest.DONE;
  263. self._responseURL = self._url;
  264. const responseType = self._responseType || 'text';
  265. // 设置响应类型
  266. switch (responseType) {
  267. case 'json':
  268. return response.json().then((json) => {
  269. self._responseText = JSON.stringify(json);
  270. self._response = json;
  271. self._onreadystatechange && self._onreadystatechange();
  272. self._onload && self._onload();
  273. self._onloadend && self._onloadend();
  274. });
  275. case 'text':
  276. return response.text().then((text) => {
  277. self._responseText = text;
  278. self._response = text;
  279. self._onreadystatechange && self._onreadystatechange();
  280. self._onload && self._onload();
  281. self._onloadend && self._onloadend();
  282. });
  283. case 'blob':
  284. return response.blob().then((blob) => {
  285. self._response = blob;
  286. self._onreadystatechange && self._onreadystatechange();
  287. self._onload && self._onload();
  288. self._onloadend && self._onloadend();
  289. });
  290. }
  291. })
  292. .catch((error) => {
  293. self._readyState = XMLHttpRequest.DONE;
  294. self._status = 0;
  295. self._statusText = 'Network Error';
  296. self._onreadystatechange && self._onreadystatechange();
  297. self._onload && self._onload();
  298. });
  299. }
  300.  
  301. setRequestHeader(name, value) {
  302. this._headers[name] = value;
  303. return this;
  304. }
  305.  
  306. getResponseHeader(name) {
  307. return this._responseHeaders ? this._responseHeaders.get(name) : null;
  308. }
  309.  
  310. getAllResponseHeaders() {
  311. return this._responseHeaders
  312. .entries()
  313. .reduce((result, [name, value]) => {
  314. return result + `${name}: ${value}\r\n`;
  315. }, '');
  316. }
  317.  
  318. set onreadystatechange(callback) {
  319. this._onreadystatechange = callback;
  320. }
  321.  
  322. set onload(callback) {
  323. this._onload = callback;
  324. }
  325.  
  326. set onloadend(callback) {
  327. this._onloadend = callback;
  328. }
  329.  
  330. get readyState() {
  331. return this._readyState;
  332. }
  333.  
  334. set readyState(state) {
  335. this._readyState = state;
  336. }
  337.  
  338. get response() {
  339. return this._response;
  340. }
  341.  
  342. set response(value) {
  343. this._response = value;
  344. }
  345.  
  346. get responseText() {
  347. return this._responseText;
  348. }
  349.  
  350. set responseText(value) {
  351. this._responseText = value;
  352. }
  353.  
  354. get responseURL() {
  355. return this._responseURL;
  356. }
  357.  
  358. set responseURL(value) {
  359. this._responseURL = value;
  360. }
  361.  
  362. get status() {
  363. return this._status;
  364. }
  365.  
  366. set status(value) {
  367. this._status = value;
  368. }
  369.  
  370. get statusText() {
  371. return this._statusText;
  372. }
  373.  
  374. set statusText(value) {
  375. this._statusText = value;
  376. }
  377.  
  378. get responseType() {
  379. return this._responseType;
  380. }
  381.  
  382. set responseType(type) {
  383. this._responseType = type;
  384. }
  385. };
  386. }
  387.  
  388. function downloadCSV(arrayOfData, filename) {
  389. // 处理数据,使其适合 CSV 格式
  390. const csvContent = arrayOfData
  391. .map((row) =>
  392. row
  393. .map((cell) => {
  394. const finalCell =
  395. typeof cell === 'number' ? cell.toString() : cell || '';
  396. return `"${finalCell.replace(/"/g, '""')}"`;
  397. })
  398. .join(',')
  399. )
  400. .join('\n');
  401.  
  402. // 在 CSV 内容前加上 BOM
  403. const bom = '\uFEFF';
  404. const csvContentWithBOM = bom + csvContent;
  405.  
  406. // 将内容转换为 Blob
  407. const blob = new Blob([csvContentWithBOM], {
  408. type: 'text/csv;charset=utf-8;',
  409. });
  410.  
  411. // 创建一个隐藏的可下载链接
  412. const url = URL.createObjectURL(blob);
  413. const link = document.createElement('a');
  414. link.href = url;
  415. link.setAttribute('download', `${filename}.csv`); // 指定文件名
  416. document.body.appendChild(link);
  417. link.click(); // 触发点击事件
  418. document.body.removeChild(link); // 清除链接
  419. URL.revokeObjectURL(url); // 释放 URL 对象
  420. }
  421.  
  422. // ################### 加载前插入样式覆盖
  423. const style = document.createElement('style');
  424. const cssRules = `
  425. .dropdown-submenu--viewmode {
  426. display: none !important;
  427. }
  428. [field=modified] {
  429. display: none !important;
  430. }
  431.  
  432. [data-value=modified] {
  433. display: none !important;
  434. }
  435. [data-value=lastmodify] {
  436. display: none !important;
  437. }
  438.  
  439. [data-grid-field=modified] {
  440. display: none !important;
  441. }
  442.  
  443. [data-field-key=modified] {
  444. display: none !important;
  445. }
  446.  
  447. #Revisions {
  448. display: none !important;
  449. }
  450.  
  451. #ContentModified {
  452. display: none !important;
  453. }
  454.  
  455. [title="最后修改时间"] {
  456. display: none !important;
  457. }
  458.  
  459. .left-tree-bottom__manager-company--wide {
  460. display: none !important;
  461. }
  462.  
  463. .left-tree-narrow .left-tree-bottom__personal--icons > a:nth-child(1) {
  464. display: none !important;
  465. }
  466.  
  467. .data_handover_card {
  468. display: none !important;
  469. }
  470.  
  471. .dtd-select-item-option[label='管理组'] {
  472. display: none !important;
  473. }
  474.  
  475. .data-manage-bar-actions > div:nth-child(2) > div.ant-space-item:nth-child(2) {
  476. display: none !important;
  477. }
  478.  
  479. .data-manage-bar-actions > div:nth-child(2) > div.ant-space-item:nth-child(3) {
  480. display: none !important;
  481. }
  482.  
  483. .approve-box .pure-form-container .department-field-view {
  484. display: none !important;
  485. }
  486.  
  487. .approve-box .pure-form-container > div:nth-child(2) {
  488. padding: 0 !important;
  489. }
  490. `;
  491. style.appendChild(document.createTextNode(cssRules));
  492. unsafeWindow.document.head.appendChild(style);
  493.  
  494. // ################### 网页内容加载完成立即执行脚本
  495. unsafeWindow.addEventListener('DOMContentLoaded', function () {
  496. // 监听任务右侧基本信息
  497. const taskRightInfoEles =
  498. unsafeWindow.document.querySelectorAll('#ContentModified');
  499. taskRightInfoEles.forEach((element) => {
  500. const parentDiv = element.closest('div.left_3_col');
  501. if (parentDiv) {
  502. parentDiv.style.display = 'none';
  503. }
  504. });
  505. });
  506.  
  507. // ################### 加载完成动态监听
  508. unsafeWindow.addEventListener('load', function () {
  509. registerMutationObserver(
  510. unsafeWindow.document.body,
  511. {
  512. attributes: false,
  513. childList: true,
  514. subtree: true,
  515. },
  516. {
  517. childList: {
  518. addedNodes: [
  519. // 动态文本替换问题
  520. {
  521. filter: (node, _mutation) => {
  522. return node.textContent.includes('最后修改时间');
  523. },
  524. action: (node, _mutation) => {
  525. replaceTextInNode(node, '最后修改时间', '迭代修改时间');
  526. },
  527. },
  528. // 监听动态弹窗 隐藏设置列表字段-最后修改时间左侧
  529. {
  530. filter: (node, _mutation) => {
  531. return (
  532. node.querySelectorAll('input[value=modified]').length > 0
  533. );
  534. },
  535. action: (node, _mutation) => {
  536. node
  537. .querySelectorAll('input[value=modified]')
  538. .forEach((ele) => {
  539. const parentDiv = ele.closest('div.field');
  540. if (parentDiv) {
  541. parentDiv.style.display = 'none';
  542. }
  543. });
  544. },
  545. },
  546. // 监听动态弹窗 隐藏设置列表字段-最后修改时间右侧
  547. {
  548. filter: (node, _mutation) => {
  549. return (
  550. node.querySelectorAll('span[title=最后修改时间]').length > 0
  551. );
  552. },
  553. action: (node, _mutation) => {
  554. node
  555. .querySelectorAll('span[title=最后修改时间]')
  556. .forEach((ele) => {
  557. const parentDiv = ele.closest('div[role=treeitem]');
  558. if (parentDiv) {
  559. parentDiv.style.display = 'none';
  560. }
  561. });
  562. },
  563. },
  564. // 监听企业微信管理端操作日志导出按钮
  565. {
  566. filter: (node, _mutation) => {
  567. return node.querySelectorAll('.js_export').length > 0;
  568. },
  569. action: (node, _mutation) => {
  570. function convertTimestampToTime(timestamp) {
  571. // 创建 Date 对象
  572. const date = new Date(timestamp * 1000); // Unix 时间戳是以秒为单位,而 Date 需要毫秒
  573.  
  574. // 获取小时和分钟
  575. const hours = date.getHours();
  576. const minutes = date.getMinutes();
  577.  
  578. // 确定上午还是下午
  579. const amPm = hours >= 12 ? '下午' : '上午';
  580.  
  581. // 返回格式化的字符串
  582. return `${amPm}${hours}:${minutes
  583. .toString()
  584. .padStart(2, '0')}`;
  585. }
  586. node.querySelectorAll('.js_export').forEach((ele) => {
  587. if (ele.dataset.eventListener === 'true') {
  588. return;
  589. }
  590. ele.dataset.eventListener = 'true';
  591. ele.addEventListener('click', async function (event) {
  592. event.preventDefault();
  593. event.stopPropagation();
  594. const response = await unsafeWindow.fetch(
  595. ...unsafeWindow.fetchCacheMap['getAdminOperationRecord']
  596. );
  597. const responseJson = await response.json();
  598. const excelData = responseJson.data.operloglist.reduce(
  599. (result, current) => {
  600. const typeMapping = {
  601. 9: '新增部门',
  602. 10: '删除部门',
  603. 11: '移动部门',
  604. 13: '删除成员',
  605. 14: '新增成员',
  606. 15: '更改成员信息',
  607. 21: '更改部门信息',
  608. 23: '登录(不可用)后台',
  609. 25: '发送邀请',
  610. 36: '修改管理组管理员列表',
  611. 35: '修改管理组应用权限',
  612. 34: '修改管理组通讯录权限',
  613. 88: '修改汇报规则',
  614. 120: '导出相关操作记录',
  615. 162: '批量设置成员信息',
  616. };
  617. const optTypeArray = {
  618. 0: '全部',
  619. 3: '成员与部门变更',
  620. 2: '权限管理变更',
  621. 12: '企业信息管理',
  622. 11: '通讯录与聊天管理',
  623. 13: '外部联系人管理',
  624. 8: '应用变更',
  625. 7: '其他',
  626. };
  627. return [
  628. ...result,
  629. [
  630. convertTimestampToTime(current.operatetime),
  631. current.op_name,
  632. optTypeArray[current.type_oper_1],
  633. typeMapping[current.type] || '其他',
  634. current.data,
  635. current.ip,
  636. ],
  637. ];
  638. },
  639. [
  640. [
  641. '时间',
  642. '操作者',
  643. '操作类型',
  644. '操作行为',
  645. '相关数据',
  646. '操作者IP',
  647. ],
  648. ]
  649. );
  650. downloadCSV(excelData, '管理端操作记录');
  651. });
  652. });
  653. },
  654. },
  655. // 监听企业微信应用使用分析导出按钮
  656. {
  657. filter: (node, _mutation) => {
  658. return (
  659. node.querySelectorAll('.log_appUse_export_button').length > 0
  660. );
  661. },
  662. action: (node, _mutation) => {
  663. node
  664. .querySelectorAll('.log_appUse_export_button')
  665. .forEach((ele) => {
  666. if (ele.dataset.eventListener === 'true') {
  667. return;
  668. }
  669. ele.dataset.eventListener = 'true';
  670. ele.addEventListener('click', async function (event) {
  671. event.preventDefault();
  672. event.stopPropagation();
  673. const response = await unsafeWindow.fetch(
  674. ...unsafeWindow.fetchCacheMap['apps']
  675. );
  676. const responseJson = await response.json();
  677. const excelData = responseJson.data.reduce(
  678. (result, current) => {
  679. return [
  680. ...result,
  681. [
  682. current.name,
  683. current.uv,
  684. current.pv,
  685. current.msg_cnt,
  686. current.cnt,
  687. ],
  688. ];
  689. },
  690. [
  691. [
  692. '应用名称',
  693. '进入人数(人)',
  694. '进入次数(次)',
  695. '发送消息数(次)',
  696. '应用可见人数(人)',
  697. ],
  698. ]
  699. );
  700. downloadCSV(excelData, '应用使用分析');
  701. });
  702. });
  703. },
  704. },
  705. // 监听钉钉首页-部门修改次数
  706. {
  707. filter: (node, _mutation) => {
  708. const spanDoms = Array.from(
  709. node.querySelectorAll('.admin-panel-v2-module span')
  710. );
  711. return (
  712. spanDoms.filter((e) =>
  713. ['近1月部门修改次数'].includes(e.innerText)
  714. ).length > 0 ||
  715. spanDoms.filter((e) => e.innerText.includes('项指标预警'))
  716. .length > 0
  717. );
  718. },
  719. action: (node, _mutation) => {
  720. const spanDoms = Array.from(
  721. node.querySelectorAll('.admin-panel-v2-module span')
  722. );
  723. spanDoms
  724. .filter((e) => ['近1月部门修改次数'].includes(e.innerText))
  725. .forEach((ele) => {
  726. const parentDiv = ele.parentElement.parentElement;
  727. if (parentDiv) {
  728. parentDiv.childNodes[1].childNodes[0].innerText = 1;
  729. parentDiv.childNodes[1].childNodes[3].style.display =
  730. 'none';
  731. }
  732. });
  733. spanDoms
  734. .filter((e) => e.innerText.includes('项指标预警'))
  735. .forEach((ele) => {
  736. const parentDiv = ele.closest('div');
  737. if (parentDiv) {
  738. parentDiv.style.display = 'none';
  739. }
  740. });
  741. },
  742. },
  743. // 监听钉钉-智能人事花名册成长记录
  744. {
  745. filter: (node, _mutation) => {
  746. return (
  747. Array.from(node.querySelectorAll('.growth-recorder-list'))
  748. .length > 0
  749. );
  750. },
  751. action: (node, _mutation) => {
  752. function isAfterDate(dateString) {
  753. // 将输入的日期字符串转换为 Date 对象
  754. const inputDate = new Date(dateString);
  755.  
  756. // 创建一个表示 2024 年 8 月 12 日的 Date 对象
  757. const august12_2024 = new Date(2024, 7, 11); // 注意:月份是从 0 开始计数的
  758.  
  759. // 比较两个日期
  760. return inputDate > august12_2024;
  761. }
  762. Array.from(
  763. node.querySelectorAll('.growth-recorder-list > li')
  764. ).forEach((ele) => {
  765. const time = ele.querySelector(
  766. '.growth-recorder-time'
  767. ).innerText;
  768. const title = ele.querySelector(
  769. '.growth-recorder-c-title'
  770. ).innerText;
  771.  
  772. if (title.includes('调岗') && isAfterDate(time)) {
  773. ele.style.display = 'none';
  774. }
  775. });
  776. },
  777. },
  778. // 监听钉钉审计日志-导出按钮
  779. {
  780. filter: (node, _mutation) => {
  781. return (
  782. node.querySelectorAll(
  783. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  784. ).length > 0
  785. );
  786. },
  787. action: (node, _mutation) => {
  788. node
  789. .querySelectorAll(
  790. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  791. )
  792. .forEach((ele) => {
  793. if (ele.dataset.eventListener === 'true') {
  794. return;
  795. }
  796. ele.dataset.eventListener = 'true';
  797. ele.addEventListener('click', async function (event) {
  798. event.preventDefault();
  799. event.stopPropagation();
  800. function getAllCookies() {
  801. const cookiesArray = document.cookie.split('; ');
  802. const cookiesObj = {};
  803. cookiesArray.forEach((cookie) => {
  804. const parts = cookie.split('=');
  805. cookiesObj[parts[0]] = decodeURIComponent(parts[1]);
  806. });
  807. return cookiesObj;
  808. }
  809. const response = await unsafeWindow.fetch(
  810. ...unsafeWindow.fetchCacheMap['listOpLog']
  811. );
  812. const responseJson = await response.json();
  813. const excelData = responseJson.result.reduce(
  814. (result, current) => {
  815. return [
  816. ...result,
  817. [
  818. formatTimestamp(current.opTime),
  819. current.opName,
  820. current.object.categoryValue,
  821. current.type.categoryValue,
  822. current.content || '',
  823. ],
  824. ];
  825. },
  826. [['时间', '操作者', '事件对象', '事件类型', '详细数据']]
  827. );
  828. downloadCSV(excelData, '审计日志信息');
  829. });
  830. });
  831. },
  832. },
  833. ],
  834. },
  835. }
  836. );
  837. });
  838.  
  839. // ################### 替换请求
  840. if (!unsafeWindow.fetchCacheMap) {
  841. unsafeWindow.fetchCacheMap = new Map();
  842. }
  843. if (
  844. unsafeWindow.location.pathname.startsWith('/wework_admin') &&
  845. !unsafeWindow.location.href.includes('loginpage_wx')
  846. ) {
  847. registerFetchModifier([
  848. {
  849. test: (url, options) => {
  850. return url.includes('/wework_admin/getAdminOperationRecord');
  851. },
  852. prerequest: (url, options) => {
  853. options.body = options.body
  854. .split('&')
  855. .reduce((result, current) => {
  856. let [key, value] = current.split('=');
  857. if (key === 'limit') {
  858. value = 500;
  859. }
  860. return [...result, `${key}=${value}`];
  861. }, [])
  862. .join('&');
  863. unsafeWindow.fetchCacheMap['getAdminOperationRecord'] = [
  864. url,
  865. options,
  866. ];
  867. return [url, options];
  868. },
  869. preresponse: async (responsePromise) => {
  870. const response = await responsePromise;
  871. let responseJson = await response.json();
  872. responseJson.data.operloglist = responseJson.data.operloglist.filter(
  873. (currentData) => {
  874. if ([3, 8].includes(currentData.type_oper_1)) {
  875. return false;
  876. }
  877. const contentFilterFlag = [
  878. '曾建培',
  879. '张杨洁',
  880. '梁博心',
  881. '李铭',
  882. '刘丽平',
  883. '刘志强',
  884. '冯茜茜',
  885. '吴慧颍',
  886. '吕昱燕',
  887. '李海粤',
  888. '𡈼满',
  889. '冯艺敏',
  890. '陈祁峰',
  891. '张鹏',
  892. '黎耀豪',
  893. '孙佩文',
  894. '周琦',
  895. '李嘉龙',
  896. '李佳玮',
  897. 'TAPD',
  898. ].reduce((result, current) => {
  899. if (!result) {
  900. return false;
  901. }
  902. return !(currentData.data || '').includes(current);
  903. }, true);
  904. if (!contentFilterFlag) {
  905. return false;
  906. }
  907. return true;
  908. }
  909. );
  910. responseJson.data.total = responseJson.data.operloglist.length;
  911. return new Response(JSON.stringify(responseJson), {
  912. headers: response.headers,
  913. ok: response.ok,
  914. redirected: response.redirected,
  915. status: response.status,
  916. statusText: response.statusText,
  917. type: response.type,
  918. url: response.url,
  919. });
  920. },
  921. },
  922. {
  923. test: (url, options) => {
  924. return url.includes('/wework_admin/log/apps/msg');
  925. },
  926. prerequest: (url, options) => {
  927. unsafeWindow.fetchCacheMap['apps'] = [url, options];
  928. return [url, options];
  929. },
  930. preresponse: async (responsePromise) => {
  931. const response = await responsePromise;
  932. let responseJson = await response.json();
  933. responseJson.data = responseJson.data.filter((currentData) => {
  934. if (currentData.name.includes('TAPD')) {
  935. return false;
  936. }
  937. return true;
  938. });
  939. return new Response(JSON.stringify(responseJson), {
  940. headers: response.headers,
  941. ok: response.ok,
  942. redirected: response.redirected,
  943. status: response.status,
  944. statusText: response.statusText,
  945. type: response.type,
  946. url: response.url,
  947. });
  948. },
  949. },
  950. ]);
  951. registerXMLHttpRequestPolyfill();
  952. }
  953.  
  954. if (unsafeWindow.location.pathname.startsWith('/adminData.htm')) {
  955. registerFetchModifier([
  956. {
  957. test: (url, options) => {
  958. return url.includes('/omp/lwpV2?key=listOpLog');
  959. },
  960. prerequest: (url, options) => {
  961. let saveFlag = true;
  962. const finalUrl = url
  963. .split('&')
  964. .reduce((result, current) => {
  965. let [key, value] = current.split('=');
  966. if (key === 'args') {
  967. const parsedValue = JSON.parse(decodeURIComponent(value));
  968. if (parsedValue[1] !== 0) {
  969. parsedValue[1] = 1000;
  970. saveFlag = false;
  971. }
  972. parsedValue[2] = 1000;
  973. value = encodeURIComponent(JSON.stringify(parsedValue));
  974. }
  975. return [...result, `${key}=${value}`];
  976. }, [])
  977. .join('&');
  978. if (saveFlag) {
  979. unsafeWindow.fetchCacheMap['listOpLog'] = [url, options];
  980. }
  981. return [finalUrl, options];
  982. },
  983. preresponse: async (responsePromise) => {
  984. const response = await responsePromise;
  985. let responseJson = await response.json();
  986. responseJson.result = responseJson.result.filter((currentData) => {
  987. if (
  988. ['删除部门', '添加部门', '部门名称修改'].includes(
  989. currentData.type.categoryValue
  990. )
  991. ) {
  992. return false;
  993. }
  994. if (
  995. ['微应用修改'].includes(currentData.type.categoryValue) &&
  996. ['马浩然', '曹宁'].includes(currentData.opName)
  997. ) {
  998. return false;
  999. }
  1000. if (
  1001. ['智能人事', '管理组'].includes(currentData.object.categoryValue)
  1002. ) {
  1003. return false;
  1004. }
  1005. if (
  1006. ['通讯录'].includes(currentData.object.categoryValue) &&
  1007. formatTimestamp(currentData.opTime).includes('2024-08-20')
  1008. ) {
  1009. return false;
  1010. }
  1011.  
  1012. const contentFilterFlag = [
  1013. '曾建培',
  1014. '张杨洁',
  1015. '梁博心',
  1016. '李铭',
  1017. '刘丽平',
  1018. '刘志强',
  1019. '冯茜茜',
  1020. '吴慧颍',
  1021. '吕昱燕',
  1022. '李海粤',
  1023. '𡈼满',
  1024. '冯艺敏',
  1025. '陈祁峰',
  1026. '张鹏',
  1027. '黎耀豪',
  1028. '孙佩文',
  1029. '周琦',
  1030. '李嘉龙',
  1031. '李佳玮',
  1032. ].reduce((result, current) => {
  1033. if (!result) {
  1034. return false;
  1035. }
  1036. return !(currentData.content || '').includes(current);
  1037. }, true);
  1038. if (!contentFilterFlag) {
  1039. return false;
  1040. }
  1041. return true;
  1042. });
  1043. return new Response(JSON.stringify(responseJson), {
  1044. headers: response.headers,
  1045. ok: response.ok,
  1046. redirected: response.redirected,
  1047. status: response.status,
  1048. statusText: response.statusText,
  1049. type: response.type,
  1050. url: response.url,
  1051. });
  1052. },
  1053. },
  1054. ]);
  1055. registerXMLHttpRequestPolyfill();
  1056. }
  1057. })();

QingJ © 2025

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