链工宝检测人脸钉钉通知

本文介绍如何使用油猴脚本来检测链工宝平台的人脸识别弹窗,并通过钉钉机器人及时通知用户进行人脸识别验证。

培训需要刷学时,学习平台为链工宝https://www.lgb360.com/,点击"个人入口"可通过扫码登录,账号密码登录,验证码登录等方式登录;
image-20251113085425614
登录后在”课程来源”处下拉选择”三岗取证培训”-选择机构-选择班级即可开始学习。(为兼容网页端防挂机措施,建议使用谷歌浏览器)
image-20251113085447556
其防挂机检测为外站的人脸识别,可以将外站链接通过钉钉提醒我们及时打卡。

链工宝检测人脸钉钉通知

本文介绍如何使用油猴脚本来检测链工宝平台的人脸识别弹窗,并通过钉钉机器人及时通知用户进行人脸识别验证。

脚本功能说明

该脚本主要实现以下功能:

  1. 自动检测链工宝网站中出现的人脸识别二维码弹窗
  2. 当检测到新的弹窗时,通过钉钉机器人发送通知消息
  3. 提供多种元素查找策略,确保在不同页面结构下都能准确识别弹窗
  4. 支持钉钉加签安全验证

代码详细讲解

参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// ==UserScript==
// @name 弹窗检测并通知钉钉(修正title获取)
// @namespace http://tampermonkey.net/
// @version 1.5
// @description 检测LGB360网站的弹窗并通过钉钉Webhook通知(正确获取title)
// @author You
// @match https://www.lgb360.com/*
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_addStyle
// @resource cryptoJS https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @connect *
// @connect cdnjs.cloudflare.com
// ==/UserScript==

(function() {
'use strict';

// 钉钉机器人配置
const DINGTALK_CONFIG = {
webhookBase: 'https://oapi.dingtalk.com/robot/send?access_token=填写webhooktoken',
secret: '填写加签秘钥'
};

// 要检测的弹窗元素XPath - 直接定位包含title的qr-code元素
const TARGET_POPUP_XPATH = '//div[contains(@class, "qr-code") and @title]';

// 备用XPath - 原来的路径
const BACKUP_POPUP_XPATH = '//*[@id="app"]/div/div[2]/div[1]/div[4]/div/div[2]/div/div[2]/div[1]//div[contains(@class, "qr-code")]';

let popupDetected = false;
let lastDetectedTitle = '';

// 加载CryptoJS(简化版)
function loadCryptoJS() {
return new Promise((resolve) => {
if (typeof CryptoJS !== 'undefined') {
resolve();
return;
}

const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js';
script.onload = resolve;
script.onerror = resolve; // 即使加载失败也继续
document.head.appendChild(script);
});
}

// 生成钉钉签名
function generateDingTalkSignature(secret) {
const timestamp = Date.now();
const stringToSign = timestamp + '\n' + secret;

if (typeof CryptoJS !== 'undefined') {
try {
const sign = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(stringToSign, secret));
const urlSafeSign = encodeURIComponent(sign);
return Promise.resolve({
timestamp: timestamp,
sign: urlSafeSign
});
} catch (error) {
console.error('CryptoJS签名错误:', error);
}
}

// 备用方案
return Promise.resolve({
timestamp: timestamp,
sign: 'direct'
});
}

// 通过XPath查找元素
function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

// 查找包含title的二维码元素
function findQrCodeWithTitle() {
// 方法1: 直接查找包含title的qr-code元素
let qrCodeElement = getElementByXpath(TARGET_POPUP_XPATH);

if (qrCodeElement) {
console.log('通过主XPath找到二维码元素:', qrCodeElement);
return qrCodeElement;
}

// 方法2: 使用备用XPath
qrCodeElement = getElementByXpath(BACKUP_POPUP_XPATH);
if (qrCodeElement) {
console.log('通过备用XPath找到二维码元素:', qrCodeElement);
// 检查这个元素是否有title
if (qrCodeElement.hasAttribute('title')) {
return qrCodeElement;
}
}

// 方法3: 使用querySelector查找
qrCodeElement = document.querySelector('.qr-code[title]');
if (qrCodeElement) {
console.log('通过CSS选择器找到二维码元素:', qrCodeElement);
return qrCodeElement;
}

// 方法4: 查找任何包含title的qr-code元素
const allQrCodes = document.querySelectorAll('.qr-code');
for (let element of allQrCodes) {
if (element.hasAttribute('title')) {
console.log('遍历找到包含title的二维码元素:', element);
return element;
}
}

return null;
}

// 获取元素的详细信息
function getElementDetails(element) {
if (!element) return null;

const title = element.getAttribute('title');
console.log('获取到的title属性:', title);

const details = {
outerHTML: element.outerHTML.substring(0, 500) + (element.outerHTML.length > 500 ? '...' : ''),
tagName: element.tagName,
className: element.className,
id: element.id,
title: title || '无',
hasTitle: element.hasAttribute('title'),
attributes: {},
childrenCount: element.children.length,
computedStyle: {}
};

// 获取所有属性
for (let attr of element.attributes) {
details.attributes[attr.name] = attr.value;
}

// 获取计算样式
try {
const style = window.getComputedStyle(element);
details.computedStyle = {
display: style.display,
visibility: style.visibility,
opacity: style.opacity
};
} catch (error) {
details.computedStyle = { error: '无法获取样式' };
}

return details;
}

// 格式化元素信息为可读文本
function formatElementInfo(details) {
if (!details) return '未找到有效元素信息';

let text = `**元素标签**: ${details.tagName}\n`;
text += `**CSS类名**: ${details.className}\n`;
text += `**元素ID**: ${details.id || '无'}\n`;
text += `**包含title属性**: ${details.hasTitle ? '是' : '否'}\n`;
text += `**title值**: ${details.title}\n`;
text += `**显示状态**: ${details.computedStyle.display || '未知'}\n`;
text += `**可见性**: ${details.computedStyle.visibility || '未知'}\n`;
text += `**子元素数量**: ${details.childrenCount}\n\n`;

// 属性信息
if (Object.keys(details.attributes).length > 0) {
text += "**属性列表**:\n";
for (let [key, value] of Object.entries(details.attributes)) {
text += `- ${key}: ${value}\n`;
}
text += "\n";
}

return text;
}

// 检查弹窗函数
function checkPopup() {
const qrCodeElement = findQrCodeWithTitle();

if (qrCodeElement) {
const currentTitle = qrCodeElement.getAttribute('title');
console.log('当前检测到的title:', currentTitle);

if (currentTitle && currentTitle !== lastDetectedTitle) {
popupDetected = true;
lastDetectedTitle = currentTitle;

console.log('检测到新的二维码弹窗,title:', currentTitle);

// 获取元素详细信息
const elementDetails = getElementDetails(qrCodeElement);
console.log('元素详细信息:', elementDetails);

// 获取当前页面信息
const pageTitle = document.title;
const pageUrl = window.location.href;

// 发送到钉钉
sendToDingTalk(pageTitle, pageUrl, elementDetails);
}
} else if (popupDetected) {
// 弹窗消失,重置状态
popupDetected = false;
lastDetectedTitle = '';
console.log('弹窗已关闭');
}
}

// 发送消息到钉钉
function sendToDingTalk(pageTitle, pageUrl, elementDetails) {
const formattedInfo = formatElementInfo(elementDetails);

// 构建更详细的消息
const qrCodeUrl = elementDetails.title;
let messageText = `## 网页弹窗检测通知\n\n`;
// messageText += `**页面标题**: ${pageTitle}\n\n`;
// messageText += `**页面网址**: ${pageUrl}\n\n`;
// messageText += `**二维码链接**: ${qrCodeUrl}\n\n`;
// messageText += `**检测时间**: ${new Date().toLocaleString()}\n\n`;
// messageText += `### 弹窗元素详细信息\n\n${formattedInfo}\n\n`;
messageText += `⚠️ 检测到人脸识别二维码弹窗,请及时处理!\n\n`;
messageText += `**直接访问链接**: ${qrCodeUrl}`;

const message = {
"msgtype": "markdown",
"markdown": {
"title": "二维码弹窗检测通知",
"text": messageText
},
"at": {
"isAtAll": false
}
};

sendDingTalkMessage(message);
}

// 发送钉钉消息
function sendDingTalkMessage(message) {
generateDingTalkSignature(DINGTALK_CONFIG.secret).then(signatureData => {
let webhookUrl = DINGTALK_CONFIG.webhookBase;
if (signatureData.sign !== 'direct') {
webhookUrl += `&timestamp=${signatureData.timestamp}&sign=${signatureData.sign}`;
}

GM_xmlhttpRequest({
method: 'POST',
url: webhookUrl,
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify(message),
onload: function(response) {
if (response.status === 200) {
console.log('钉钉通知发送成功');
} else {
console.error('钉钉通知发送失败:', response.status, response.responseText);
}
},
onerror: function(error) {
console.error('发送请求时出错:', error);
}
});
}).catch(error => {
console.error('生成签名时出错:', error);
});
}

// 使用MutationObserver监听DOM变化
function startMonitoring() {
console.log('开始监控二维码弹窗...');

// 立即检查一次
checkPopup();

// 创建观察器实例
const observer = new MutationObserver(function(mutations) {
let shouldCheck = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) { // 元素节点
if (node.matches && node.matches('.qr-code')) {
shouldCheck = true;
} else if (node.querySelector) {
const hasQrCode = node.querySelector('.qr-code');
if (hasQrCode) shouldCheck = true;
}
}
});
}
});
if (shouldCheck) {
setTimeout(checkPopup, 500); // 延迟检查确保DOM完全加载
}
});

// 开始观察整个document
observer.observe(document.body, {
childList: true,
subtree: true
});

// 同时使用定时器作为备用检测
setInterval(checkPopup, 3000);
}

// 初始化函数
function init() {
loadCryptoJS().then(() => {
console.log('初始化完成,开始监控');
startMonitoring();
});
}

// 页面加载完成后开始初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}

// 添加调试函数
window.debugQrCode = function() {
console.log('=== 二维码元素调试信息 ===');

// 尝试所有查找方法
const methods = [
{ name: '主XPath', element: getElementByXpath(TARGET_POPUP_XPATH) },
{ name: '备用XPath', element: getElementByXpath(BACKUP_POPUP_XPATH) },
{ name: 'CSS选择器', element: document.querySelector('.qr-code[title]') },
{ name: '所有qr-code', elements: document.querySelectorAll('.qr-code') }
];

methods.forEach(method => {
console.log(`\n${method.name}:`);
if (method.elements) {
console.log(`找到 ${method.elements.length} 个.qr-code元素`);
method.elements.forEach((el, index) => {
const title = el.getAttribute('title');
console.log(` ${index + 1}. ${el.outerHTML.substring(0, 200)}`);
console.log(` title: ${title}`);
});
} else if (method.element) {
const title = method.element.getAttribute('title');
console.log(`找到元素:`, method.element);
console.log(`outerHTML:`, method.element.outerHTML);
console.log(`title属性:`, title);
} else {
console.log('未找到元素');
}
});
};
})();

钉钉机器人配置说明

要使用此脚本,您需要先在钉钉中创建一个机器人并获取Webhook地址和加签密钥:

  1. 打开钉钉,进入需要接收通知的群聊
  2. 点击右上角”群设置” → “智能群助手”
  3. 点击”添加机器人” → 选择”自定义”
  4. 设置机器人名称,安全设置选择”加签”,复制加签密钥
  5. 完成创建后,复制Webhook地址中的access_token部分

将获取到的信息填入脚本中的配置部分:

1
2
3
4
const DINGTALK_CONFIG = {
webhookBase: 'https://oapi.dingtalk.com/robot/send?access_token=你的access_token',
secret: '你的加签密钥'
};

脚本工作原理

  1. 元素检测机制:脚本使用多种策略来查找页面中的人脸识别二维码弹窗:

    • 通过XPath直接查找包含title属性的qr-code元素
    • 使用备用XPath路径查找元素
    • 使用CSS选择器查找元素
    • 遍历所有qr-code元素查找包含title属性的元素
  2. 变化监听机制

    • 使用MutationObserver监听DOM变化,当有新的qr-code元素添加到页面时立即检测
    • 同时使用定时器定期检查,确保不会遗漏弹窗
  3. 消息通知机制

    • 当检测到新的弹窗时(通过title值判断是否为新弹窗),发送钉钉通知
    • 使用CryptoJS库生成加签,确保消息安全性
    • 通知中包含二维码链接,可直接点击访问

使用步骤

  1. 安装油猴插件(Tampermonkey)
  2. 在钉钉中创建自定义机器人,获取Webhook地址和加签密钥
  3. 在油猴中创建新脚本,将上述代码复制进去
  4. 修改脚本中的webhookBase和secret为你的钉钉机器人信息
  5. 保存脚本并确保在链工宝网站上启用
  6. 打开链工宝网站开始学习,当出现人脸识别弹窗时会收到钉钉通知

注意事项

  1. 请确保使用Chrome浏览器以获得最佳兼容性
  2. 脚本只在链工宝网站(https://www.lgb360.com/*)上运行
  3. 由于链工宝网站可能会更新页面结构,如果脚本失效,请检查XPath路径是否需要调整
  4. 钉钉机器人的加签密钥请妥善保管,不要泄露
  5. 脚本会定期检查页面元素,可能会对页面性能产生轻微影响

可能遇到的问题及解决方法

  1. 收不到通知

    • 检查钉钉机器人配置是否正确
    • 检查控制台是否有错误信息
    • 使用window.debugQrCode()函数在控制台中调试元素查找
  2. 元素查找失败

    • 链工宝网站更新了页面结构,需要更新XPath路径
    • 可以通过浏览器开发者工具检查qr-code元素的实际路径
  3. 钉钉消息发送失败

    • 检查网络连接是否正常
    • 确认钉钉机器人的安全设置是否正确配置
    • 检查access_token是否正确
  4. 脚本不运行

    • 确认油猴插件已正确安装并启用
    • 检查脚本的@match规则是否匹配当前网址
    • 确认脚本在油猴中已启用

通过以上配置和使用说明,您可以成功使用该脚本来监控链工宝平台的人脸识别弹窗,并及时收到钉钉通知。

安全声明

⚠️ 重要声明

  1. 本脚本仅供学习和研究目的使用,旨在帮助理解网页自动化技术和通知机制的实现原理。

  2. 用户在使用本脚本时,必须确保遵守链工宝平台的用户协议和相关服务条款,不得用于任何违反平台规定的行为。

  3. 任何使用本脚本进行非法用途的行为均与作者无关,用户需自行承担相应的法律责任。

  4. 请严格遵守《中华人民共和国网络安全法》、《计算机软件保护条例》等相关法律法规,合法合规地使用技术工具。

  5. 建议用户在使用前充分了解相关平台的反爬虫机制和使用规范,避免对平台造成不必要的负担或违反服务条款。

  6. 作者不对因使用本脚本而导致的任何直接或间接损失负责,包括但不限于账号封禁、数据丢失、财产损失等。

请在合法合规的前提下,合理使用技术工具进行学习和研究。


链工宝检测人脸钉钉通知
https://miku2024.top/posts/链工宝检测人脸钉钉通知/
作者
KB
发布于
2025年11月13日
许可协议