MonkeyModifier

Change webpage content

当前为 2024-08-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MonkeyModifier
  3. // @namespace https://github.com/JiyuShao/greasyfork-scripts
  4. // @version 2024-08-20
  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.map((cell) => {
  393. const finalCell = typeof cell === 'number' ? cell.toString() : (cell || '');
  394. return `"${(finalCell).replace(/"/g, '""')}"`;
  395. }).join(',')
  396. )
  397. .join('\n');
  398.  
  399. // 在 CSV 内容前加上 BOM
  400. const bom = '\uFEFF';
  401. const csvContentWithBOM = bom + csvContent;
  402.  
  403. // 将内容转换为 Blob
  404. const blob = new Blob([csvContentWithBOM], {
  405. type: 'text/csv;charset=utf-8;',
  406. });
  407.  
  408. // 创建一个隐藏的可下载链接
  409. const url = URL.createObjectURL(blob);
  410. const link = document.createElement('a');
  411. link.href = url;
  412. link.setAttribute('download', `${filename}.csv`); // 指定文件名
  413. document.body.appendChild(link);
  414. link.click(); // 触发点击事件
  415. document.body.removeChild(link); // 清除链接
  416. URL.revokeObjectURL(url); // 释放 URL 对象
  417. }
  418.  
  419. // ################### 加载前插入样式覆盖
  420. const style = document.createElement('style');
  421. const cssRules = `
  422. .dropdown-submenu--viewmode {
  423. display: none !important;
  424. }
  425. [field=modified] {
  426. display: none !important;
  427. }
  428.  
  429. [data-value=modified] {
  430. display: none !important;
  431. }
  432. [data-value=lastmodify] {
  433. display: none !important;
  434. }
  435.  
  436. [data-grid-field=modified] {
  437. display: none !important;
  438. }
  439.  
  440. [data-field-key=modified] {
  441. display: none !important;
  442. }
  443.  
  444. #Revisions {
  445. display: none !important;
  446. }
  447.  
  448. #ContentModified {
  449. display: none !important;
  450. }
  451.  
  452. [title="最后修改时间"] {
  453. display: none !important;
  454. }
  455.  
  456. .left-tree-bottom__manager-company--wide {
  457. display: none !important;
  458. }
  459.  
  460. .left-tree-narrow .left-tree-bottom__personal--icons > a:nth-child(1) {
  461. display: none !important;
  462. }
  463.  
  464. .data_handover_card {
  465. display: none !important;
  466. }
  467.  
  468. .dtd-select-item-option[label='管理组'] {
  469. display: none !important;
  470. }
  471.  
  472. .data-manage-bar-actions > div:nth-child(2) > div.ant-space-item:nth-child(2) {
  473. display: none !important;
  474. }
  475.  
  476. .data-manage-bar-actions > div:nth-child(2) > div.ant-space-item:nth-child(3) {
  477. display: none !important;
  478. }
  479.  
  480. .approve-box .pure-form-container .department-field-view {
  481. display: none !important;
  482. }
  483.  
  484. .approve-box .pure-form-container > div:nth-child(2) {
  485. padding: 0 !important;
  486. }
  487. `;
  488. style.appendChild(document.createTextNode(cssRules));
  489. unsafeWindow.document.head.appendChild(style);
  490.  
  491. // ################### 网页内容加载完成立即执行脚本
  492. unsafeWindow.addEventListener('DOMContentLoaded', function () {
  493. // 监听任务右侧基本信息
  494. const taskRightInfoEles =
  495. unsafeWindow.document.querySelectorAll('#ContentModified');
  496. taskRightInfoEles.forEach((element) => {
  497. const parentDiv = element.closest('div.left_3_col');
  498. if (parentDiv) {
  499. parentDiv.style.display = 'none';
  500. }
  501. });
  502. });
  503.  
  504. // ################### 加载完成动态监听
  505. unsafeWindow.addEventListener('load', function () {
  506. registerMutationObserver(
  507. unsafeWindow.document.body,
  508. {
  509. attributes: false,
  510. childList: true,
  511. subtree: true,
  512. },
  513. {
  514. childList: {
  515. addedNodes: [
  516. // 动态文本替换问题
  517. {
  518. filter: (node, _mutation) => {
  519. return node.textContent.includes('最后修改时间');
  520. },
  521. action: (node, _mutation) => {
  522. replaceTextInNode(node, '最后修改时间', '迭代修改时间');
  523. },
  524. },
  525. // 监听动态弹窗 隐藏设置列表字段-最后修改时间左侧
  526. {
  527. filter: (node, _mutation) => {
  528. return (
  529. node.querySelectorAll('input[value=modified]').length > 0
  530. );
  531. },
  532. action: (node, _mutation) => {
  533. node
  534. .querySelectorAll('input[value=modified]')
  535. .forEach((ele) => {
  536. const parentDiv = ele.closest('div.field');
  537. if (parentDiv) {
  538. parentDiv.style.display = 'none';
  539. }
  540. });
  541. },
  542. },
  543. // 监听动态弹窗 隐藏设置列表字段-最后修改时间右侧
  544. {
  545. filter: (node, _mutation) => {
  546. return (
  547. node.querySelectorAll('span[title=最后修改时间]').length > 0
  548. );
  549. },
  550. action: (node, _mutation) => {
  551. node
  552. .querySelectorAll('span[title=最后修改时间]')
  553. .forEach((ele) => {
  554. const parentDiv = ele.closest('div[role=treeitem]');
  555. if (parentDiv) {
  556. parentDiv.style.display = 'none';
  557. }
  558. });
  559. },
  560. },
  561. // 监听企业微信管理端操作日志导出按钮
  562. {
  563. filter: (node, _mutation) => {
  564. return node.querySelectorAll('.js_export').length > 0;
  565. },
  566. action: (node, _mutation) => {
  567. function convertTimestampToTime(timestamp) {
  568. // 创建 Date 对象
  569. const date = new Date(timestamp * 1000); // Unix 时间戳是以秒为单位,而 Date 需要毫秒
  570.  
  571. // 获取小时和分钟
  572. const hours = date.getHours();
  573. const minutes = date.getMinutes();
  574.  
  575. // 确定上午还是下午
  576. const amPm = hours >= 12 ? '下午' : '上午';
  577.  
  578. // 返回格式化的字符串
  579. return `${amPm}${hours}:${minutes
  580. .toString()
  581. .padStart(2, '0')}`;
  582. }
  583. node.querySelectorAll('.js_export').forEach((ele) => {
  584. if (ele.dataset.eventListener === 'true') {
  585. return;
  586. }
  587. ele.dataset.eventListener = 'true';
  588. ele.addEventListener('click', async function (event) {
  589. event.preventDefault();
  590. event.stopPropagation();
  591. const response = await unsafeWindow.fetch(
  592. ...unsafeWindow.fetchCacheMap['getAdminOperationRecord']
  593. );
  594. const responseJson = await response.json();
  595. const excelData = responseJson.data.operloglist.reduce(
  596. (result, current) => {
  597. const typeMapping = {
  598. 9: '新增部门',
  599. 10: '删除部门',
  600. 11: '移动部门',
  601. 13: '删除成员',
  602. 14: '新增成员',
  603. 15: '更改成员信息',
  604. 21: '更改部门信息',
  605. 23: '登录后台',
  606. 25: '发送邀请',
  607. 36: '修改管理组管理员列表',
  608. 35: '修改管理组应用权限',
  609. 34: '修改管理组通讯录权限',
  610. 88: '修改汇报规则',
  611. 120: '导出相关操作记录',
  612. 162: '批量设置成员信息',
  613. };
  614. const optTypeArray = {
  615. 0: '全部',
  616. 3: '成员与部门变更',
  617. 2: '权限管理变更',
  618. 12: '企业信息管理',
  619. 11: '通讯录与聊天管理',
  620. 13: '外部联系人管理',
  621. 8: '应用变更',
  622. 7: '其他',
  623. };
  624. return [
  625. ...result,
  626. [
  627. convertTimestampToTime(current.operatetime),
  628. current.op_name,
  629. optTypeArray[current.type_oper_1],
  630. typeMapping[current.type] || '其他',
  631. current.data,
  632. current.ip,
  633. ],
  634. ];
  635. },
  636. [
  637. [
  638. '时间',
  639. '操作者',
  640. '操作类型',
  641. '操作行为',
  642. '相关数据',
  643. '操作者IP',
  644. ],
  645. ]
  646. );
  647. downloadCSV(excelData, '管理端操作记录');
  648. });
  649. });
  650. },
  651. },
  652. // 监听企业微信应用使用分析导出按钮
  653. {
  654. filter: (node, _mutation) => {
  655. return node.querySelectorAll('.log_appUse_export_button').length > 0;
  656. },
  657. action: (node, _mutation) => {
  658. node.querySelectorAll('.log_appUse_export_button').forEach((ele) => {
  659. if (ele.dataset.eventListener === 'true') {
  660. return;
  661. }
  662. ele.dataset.eventListener = 'true';
  663. ele.addEventListener('click', async function (event) {
  664. event.preventDefault();
  665. event.stopPropagation();
  666. const response = await unsafeWindow.fetch(
  667. ...unsafeWindow.fetchCacheMap['apps']
  668. );
  669. const responseJson = await response.json();
  670. const excelData = responseJson.data.reduce(
  671. (result, current) => {
  672. return [
  673. ...result,
  674. [
  675. current.name,
  676. current.uv,
  677. current.pv,
  678. current.msg_cnt,
  679. current.cnt,
  680. ],
  681. ];
  682. },
  683. [
  684. [
  685. '应用名称',
  686. '进入人数(人)',
  687. '进入次数(次)',
  688. '发送消息数(次)',
  689. '应用可见人数(人)',
  690. ],
  691. ]
  692. );
  693. downloadCSV(excelData, '应用使用分析');
  694. });
  695. });
  696. },
  697. },
  698. // 监听钉钉首页-部门修改次数
  699. {
  700. filter: (node, _mutation) => {
  701. const spanDoms = Array.from(
  702. node.querySelectorAll('.admin-panel-v2-module span')
  703. );
  704. return (
  705. spanDoms.filter((e) =>
  706. ['近1月部门修改次数'].includes(e.innerText)
  707. ).length > 0 ||
  708. spanDoms.filter((e) => e.innerText.includes('项指标预警'))
  709. .length > 0
  710. );
  711. },
  712. action: (node, _mutation) => {
  713. const spanDoms = Array.from(
  714. node.querySelectorAll('.admin-panel-v2-module span')
  715. );
  716. spanDoms
  717. .filter((e) => ['近1月部门修改次数'].includes(e.innerText))
  718. .forEach((ele) => {
  719. const parentDiv = ele.parentElement.parentElement;
  720. if (parentDiv) {
  721. parentDiv.childNodes[1].childNodes[0].innerText = 1;
  722. parentDiv.childNodes[1].childNodes[3].style.display =
  723. 'none';
  724. }
  725. });
  726. spanDoms
  727. .filter((e) => e.innerText.includes('项指标预警'))
  728. .forEach((ele) => {
  729. const parentDiv = ele.closest('div');
  730. if (parentDiv) {
  731. parentDiv.style.display = 'none';
  732. }
  733. });
  734. },
  735. },
  736. // 监听钉钉-智能人事花名册成长记录
  737. {
  738. filter: (node, _mutation) => {
  739. return (
  740. Array.from(node.querySelectorAll('.growth-recorder-list'))
  741. .length > 0
  742. );
  743. },
  744. action: (node, _mutation) => {
  745. function isAfterDate(dateString) {
  746. // 将输入的日期字符串转换为 Date 对象
  747. const inputDate = new Date(dateString);
  748.  
  749. // 创建一个表示 2024 年 8 月 12 日的 Date 对象
  750. const august12_2024 = new Date(2024, 7, 11); // 注意:月份是从 0 开始计数的
  751.  
  752. // 比较两个日期
  753. return inputDate > august12_2024;
  754. }
  755. Array.from(
  756. node.querySelectorAll('.growth-recorder-list > li')
  757. ).forEach((ele) => {
  758. const time = ele.querySelector(
  759. '.growth-recorder-time'
  760. ).innerText;
  761. const title = ele.querySelector(
  762. '.growth-recorder-c-title'
  763. ).innerText;
  764.  
  765. if (title.includes('调岗') && isAfterDate(time)) {
  766. ele.style.display = 'none';
  767. }
  768. });
  769. },
  770. },
  771. // 监听钉钉审计日志-导出按钮
  772. {
  773. filter: (node, _mutation) => {
  774. return (
  775. node.querySelectorAll(
  776. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  777. ).length > 0
  778. );
  779. },
  780. action: (node, _mutation) => {
  781. node
  782. .querySelectorAll(
  783. '.audit-content .dd-toolbar-btns-container .dd-toolbar-action-btns > div:nth-child(2) button'
  784. )
  785. .forEach((ele) => {
  786. if (ele.dataset.eventListener === 'true') {
  787. return;
  788. }
  789. ele.dataset.eventListener = 'true';
  790. ele.addEventListener('click', async function (event) {
  791. event.preventDefault();
  792. event.stopPropagation();
  793. function getAllCookies() {
  794. const cookiesArray = document.cookie.split('; ');
  795. const cookiesObj = {};
  796. cookiesArray.forEach((cookie) => {
  797. const parts = cookie.split('=');
  798. cookiesObj[parts[0]] = decodeURIComponent(parts[1]);
  799. });
  800. return cookiesObj;
  801. }
  802. const response = await unsafeWindow.fetch(
  803. ...unsafeWindow.fetchCacheMap['listOpLog']
  804. );
  805. const responseJson = await response.json();
  806. const excelData = responseJson.result.reduce(
  807. (result, current) => {
  808. return [
  809. ...result,
  810. [
  811. formatTimestamp(current.opTime),
  812. current.opName,
  813. current.object.categoryValue,
  814. current.type.categoryValue,
  815. current.content || '',
  816. ],
  817. ];
  818. },
  819. [['时间', '操作者', '事件对象', '事件类型', '详细数据']]
  820. );
  821. downloadCSV(excelData, '审计日志信息');
  822. });
  823. });
  824. },
  825. },
  826. ],
  827. },
  828. }
  829. );
  830. });
  831.  
  832. // ################### 替换请求
  833. if (!unsafeWindow.fetchCacheMap) {
  834. unsafeWindow.fetchCacheMap = new Map();
  835. }
  836. if (
  837. unsafeWindow.location.pathname.startsWith('/wework_admin') &&
  838. !unsafeWindow.location.href.includes('loginpage_wx')
  839. ) {
  840. registerFetchModifier([
  841. {
  842. test: (url, options) => {
  843. return url.includes('/wework_admin/getAdminOperationRecord');
  844. },
  845. prerequest: (url, options) => {
  846. options.body = options.body
  847. .split('&')
  848. .reduce((result, current) => {
  849. let [key, value] = current.split('=');
  850. if (key === 'limit') {
  851. value = 500;
  852. }
  853. return [...result, `${key}=${value}`];
  854. }, [])
  855. .join('&');
  856. unsafeWindow.fetchCacheMap['getAdminOperationRecord'] = [url, options];
  857. return [url, options];
  858. },
  859. preresponse: async (responsePromise) => {
  860. const response = await responsePromise;
  861. let responseJson = await response.json();
  862. responseJson.data.operloglist = responseJson.data.operloglist.filter(
  863. (currentData) => {
  864. if (currentData.type_oper_1 === 3) {
  865. return false;
  866. }
  867. const contentFilterFlag = [
  868. '曾建培',
  869. '张杨洁',
  870. '梁博心',
  871. '李铭',
  872. '刘丽平',
  873. '刘志强',
  874. '冯茜茜',
  875. '吴慧颍',
  876. '吕昱燕',
  877. '李海粤',
  878. '𡈼满',
  879. '冯艺敏',
  880. '陈祁峰',
  881. '张鹏',
  882. '黎耀豪',
  883. '孙佩文',
  884. '周琦',
  885. '李嘉龙',
  886. '李佳玮',
  887. 'TAPD',
  888. ].reduce((result, current) => {
  889. if (!result) {
  890. return false;
  891. }
  892. return !(currentData.data || '').includes(current);
  893. }, true);
  894. if (!contentFilterFlag) {
  895. return false;
  896. }
  897. return true;
  898. }
  899. );
  900. responseJson.data.total = responseJson.data.operloglist.length;
  901. return new Response(JSON.stringify(responseJson), {
  902. headers: response.headers,
  903. ok: response.ok,
  904. redirected: response.redirected,
  905. status: response.status,
  906. statusText: response.statusText,
  907. type: response.type,
  908. url: response.url,
  909. });
  910. },
  911. }, {
  912. test: (url, options) => {
  913. return url.includes('/wework_admin/log/apps/msg');
  914. },
  915. prerequest: (url, options) => {
  916. unsafeWindow.fetchCacheMap['apps'] = [url, options];
  917. return [url, options];
  918. },
  919. preresponse: async (responsePromise) => {
  920. const response = await responsePromise;
  921. let responseJson = await response.json();
  922. responseJson.data = responseJson.data.filter(
  923. (currentData) => {
  924. if (currentData.name.includes('TAPD')) {
  925. return false;
  926. }
  927. return true;
  928. }
  929. );
  930. return new Response(JSON.stringify(responseJson), {
  931. headers: response.headers,
  932. ok: response.ok,
  933. redirected: response.redirected,
  934. status: response.status,
  935. statusText: response.statusText,
  936. type: response.type,
  937. url: response.url,
  938. });
  939. },
  940. },
  941. ]);
  942. registerXMLHttpRequestPolyfill();
  943. }
  944.  
  945. if (unsafeWindow.location.pathname.startsWith('/adminData.htm')) {
  946. registerFetchModifier([
  947. {
  948. test: (url, options) => {
  949. return url.includes('/omp/lwpV2?key=listOpLog');
  950. },
  951. prerequest: (url, options) => {
  952. const finalUrl = url
  953. .split('&')
  954. .reduce((result, current) => {
  955. let [key, value] = current.split('=');
  956. if (key === 'args') {
  957. const parsedValue = JSON.parse(decodeURIComponent(value));
  958. parsedValue[1] = 0;
  959. parsedValue[2] = 1000;
  960. value = encodeURIComponent(JSON.stringify(parsedValue));
  961. }
  962. return [...result, `${key}=${value}`];
  963. }, [])
  964. .join('&');
  965. unsafeWindow.fetchCacheMap['listOpLog'] = [url, options];
  966. return [finalUrl, options];
  967. },
  968. preresponse: async (responsePromise) => {
  969. const response = await responsePromise;
  970. let responseJson = await response.json();
  971. responseJson.result = responseJson.result.filter((currentData) => {
  972. if (
  973. ['删除部门', '添加部门', '部门名称修改'].includes(
  974. currentData.type.categoryValue
  975. )
  976. ) {
  977. return false;
  978. }
  979. if (
  980. ['微应用修改'].includes(currentData.type.categoryValue) &&
  981. ['马浩然', '曹宁'].includes(currentData.opName)
  982. ) {
  983. return false;
  984. }
  985. if (
  986. ['智能人事', '管理组'].includes(currentData.object.categoryValue)
  987. ) {
  988. return false;
  989. }
  990. if (
  991. ['通讯录'].includes(currentData.object.categoryValue) &&
  992. formatTimestamp(currentData.opTime).includes('2024-08-20')
  993. ) {
  994. return false;
  995. }
  996.  
  997. const contentFilterFlag = [
  998. '曾建培',
  999. '张杨洁',
  1000. '梁博心',
  1001. '李铭',
  1002. '刘丽平',
  1003. '刘志强',
  1004. '冯茜茜',
  1005. '吴慧颍',
  1006. '吕昱燕',
  1007. '李海粤',
  1008. '𡈼满',
  1009. '冯艺敏',
  1010. '陈祁峰',
  1011. '张鹏',
  1012. '黎耀豪',
  1013. '孙佩文',
  1014. '周琦',
  1015. '李嘉龙',
  1016. '李佳玮',
  1017. ].reduce((result, current) => {
  1018. if (!result) {
  1019. return false;
  1020. }
  1021. return !(currentData.content || '').includes(current);
  1022. }, true);
  1023. if (!contentFilterFlag) {
  1024. return false;
  1025. }
  1026. return true;
  1027. });
  1028. return new Response(JSON.stringify(responseJson), {
  1029. headers: response.headers,
  1030. ok: response.ok,
  1031. redirected: response.redirected,
  1032. status: response.status,
  1033. statusText: response.statusText,
  1034. type: response.type,
  1035. url: response.url,
  1036. });
  1037. },
  1038. },
  1039. ]);
  1040. registerXMLHttpRequestPolyfill();
  1041. }
  1042. })();