Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7020866e92 | |||
| a2bb4caa74 | |||
| d45d6f4d7d | |||
| c84266f8fd | |||
| 1fa29ebd37 | |||
| ea8673540c | |||
| 228c0b9bbb | |||
| b224c75ad0 | |||
| 945388bdb5 | |||
| ae7c6c13fb | |||
| 4e174a02aa | |||
| 0719dbdf89 | |||
| c487defeed | |||
| 717896d92e | |||
| 6bc925a06b | |||
| d7e96b0b08 | |||
| 2073dd2377 | |||
| 6438e4dbd3 | |||
| c7a619503b | |||
| f591ff14ac | |||
| 0a7657f716 | |||
| 9085a3c1c4 | |||
| 539042881e | |||
| e0b1b2a4f4 | |||
| 727cee69bb | |||
| fcc9ef1b98 | |||
| 38d50c77dc | |||
| e954055042 | |||
| 0cf94d44fc | |||
| 605bc8b252 | |||
| a689e66635 | |||
| 026e06a6b3 | |||
| c98ded5250 | |||
| 02562f1c50 | |||
| 4824f17881 | |||
| 9c06034579 | |||
| e204c4a6da | |||
| 4a33094140 | |||
| 354dbe3f15 | |||
| 93f8367b37 | |||
| 244af8a290 | |||
| 0f75bc03e1 | |||
| 8786280209 | |||
| 04fbf87490 | |||
| cd0f0ebebc | |||
| 6c704d69e3 | |||
| 468fcea934 | |||
| ec18b9bf68 | |||
| 2d54c90d43 | |||
| d8cba15006 | |||
| 5b87938acf | |||
| a789a81fbd | |||
| 02d7b85190 | |||
| 8936129558 | |||
| e6282083ba | |||
| d21f64bfc2 | |||
| 474c9bf339 | |||
| 7eab86c910 | |||
| 57ae14049e | |||
| ca49028e98 | |||
| 32eb83c5d6 | |||
| 9b191be6d6 | |||
| 8a6444cb14 | |||
| 1b18804b2b | |||
| aa540ac622 | |||
| c9ee7d9a4f | |||
| f71650c347 | |||
| a690ca7fe5 | |||
| ea07867b67 | |||
| 1eedb1468e | |||
| 0870a040b6 | |||
| 8ae6a7593b | |||
| 343a14f6a2 | |||
| 62cdb55da2 |
@@ -0,0 +1,445 @@
|
|||||||
|
// JSPL的计算过程总结:
|
||||||
|
//
|
||||||
|
// 输入: 传入的会话ID或相关数据
|
||||||
|
// 哈希处理: 使用DJB2算法变种对输入进行哈希
|
||||||
|
// 种子生成: 结合时间戳和常量生成加密种子
|
||||||
|
// 数据收集: 收集各种浏览器指纹和环境信息
|
||||||
|
// 加密处理: 使用自定义的流加密算法对数据进行加密
|
||||||
|
// Base64编码: 最终使用自定义字符集进行Base64编码
|
||||||
|
// 这个函数生成的第三个返回值就是用于JSPL参数的加密字符串
|
||||||
|
var zn = (function () {
|
||||||
|
// 检查是否已经初始化
|
||||||
|
// if (Pn) {
|
||||||
|
// return Nn;
|
||||||
|
// }
|
||||||
|
|
||||||
|
Pn = true;
|
||||||
|
var n = "unknown"; // 默认返回值
|
||||||
|
var constantSeed = 11027890091; // 常量种子
|
||||||
|
var o = true; // 状态标志
|
||||||
|
|
||||||
|
// 字符串哈希函数 - DJB2算法的变种
|
||||||
|
function djb2HashString(str) {
|
||||||
|
if (!str) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
var hash = 0;
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
hash = (hash << 8) - hash + str.charCodeAt(i) | 0;
|
||||||
|
}
|
||||||
|
return hash === 0 ? n : hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 映射函数 - 用于Base64编码的自定义字符集
|
||||||
|
function mapChar(n) {
|
||||||
|
if (n > 37) {
|
||||||
|
return 59 + n;
|
||||||
|
} else if (n > 11) {
|
||||||
|
return 53 + n;
|
||||||
|
} else if (n > 1) {
|
||||||
|
return 46 + n;
|
||||||
|
} else {
|
||||||
|
return n * 50 + 45;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 线性同余生成器 - 用于生成伪随机数
|
||||||
|
function lcg(n) {
|
||||||
|
return (n = (n ^= n << 13) ^ n >> 17) ^ n << 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密随机数生成器
|
||||||
|
function createRandomGenerator(seed, initialValue) {
|
||||||
|
var state = seed;
|
||||||
|
var counter = -1;
|
||||||
|
var value = initialValue;
|
||||||
|
var firstCall = o;
|
||||||
|
o = false;
|
||||||
|
var buffer = null;
|
||||||
|
|
||||||
|
return function (peek) {
|
||||||
|
var result;
|
||||||
|
if (buffer !== null) {
|
||||||
|
result = buffer;
|
||||||
|
buffer = null;
|
||||||
|
} else {
|
||||||
|
if (++counter > 2) {
|
||||||
|
state = lcg(state);
|
||||||
|
counter = 0;
|
||||||
|
}
|
||||||
|
result = ((result = state >> 16 - counter * 8) ^ (firstCall ? --value : 0)) & 255;
|
||||||
|
if (peek) {
|
||||||
|
buffer = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主加密函数
|
||||||
|
return Nn = function (inputString, additionalData) {
|
||||||
|
// console.log("inputString:" + inputString)
|
||||||
|
// console.log("additionalData:" + additionalData)
|
||||||
|
// 混合输入数据生成种子
|
||||||
|
var seed = constantSeed ^ djb2HashString(inputString) ^ additionalData;
|
||||||
|
|
||||||
|
// 生成基于时间的随机数
|
||||||
|
var timeBasedRandom = lcg(lcg(Date.now() >> 3 ^ constantSeed) * constantSeed);
|
||||||
|
|
||||||
|
// 创建随机数生成器
|
||||||
|
var randomGenerator = createRandomGenerator(seed, timeBasedRandom);
|
||||||
|
|
||||||
|
// 存储加密数据的数组
|
||||||
|
var encryptedData = [];
|
||||||
|
var isFirstCall = true;
|
||||||
|
var callCounter = 0;
|
||||||
|
|
||||||
|
// UTF-8编码和加密函数
|
||||||
|
function utf8EncodeAndEncrypt(str) {
|
||||||
|
var bytes = [];
|
||||||
|
var byteIndex = 0;
|
||||||
|
|
||||||
|
// UTF-8编码
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
var charCode = str.charCodeAt(i);
|
||||||
|
if (charCode < 128) {
|
||||||
|
bytes[byteIndex++] = charCode;
|
||||||
|
} else {
|
||||||
|
if (charCode < 2048) {
|
||||||
|
bytes[byteIndex++] = charCode >> 6 | 192;
|
||||||
|
} else {
|
||||||
|
if ((charCode & 64512) == 55296 && i + 1 < str.length && (str.charCodeAt(i + 1) & 64512) == 56320) {
|
||||||
|
charCode = 65536 + ((charCode & 1023) << 10) + (str.charCodeAt(++i) & 1023);
|
||||||
|
bytes[byteIndex++] = charCode >> 18 | 240;
|
||||||
|
bytes[byteIndex++] = charCode >> 12 & 63 | 128;
|
||||||
|
} else {
|
||||||
|
bytes[byteIndex++] = charCode >> 12 | 224;
|
||||||
|
}
|
||||||
|
bytes[byteIndex++] = charCode >> 6 & 63 | 128;
|
||||||
|
}
|
||||||
|
bytes[byteIndex++] = charCode & 63 | 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR加密
|
||||||
|
for (var j = 0; j < bytes.length; j++) {
|
||||||
|
bytes[j] ^= randomGenerator();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON序列化函数
|
||||||
|
function safeJsonStringify(obj) {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(obj);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据收集和加密函数
|
||||||
|
function collectAndEncrypt(key, value) {
|
||||||
|
// console.log("key:" + key)
|
||||||
|
// console.log("value:" + value)
|
||||||
|
if (!(typeof key != "string" || key.length == 0 || value && ["number", "string", "boolean"].indexOf(typeof value) == -1)) {
|
||||||
|
let serializedKey = safeJsonStringify(key);
|
||||||
|
// console.log("serializedKey:" + serializedKey)
|
||||||
|
value = safeJsonStringify(value);
|
||||||
|
// console.log("serializedValue:" + value)
|
||||||
|
|
||||||
|
if (key && value !== undefined && key !== "xt1") {
|
||||||
|
// 添加分隔符和加密数据
|
||||||
|
encryptedData.push(randomGenerator() ^ (encryptedData.length ? 44 : 123));
|
||||||
|
Array.prototype.push.apply(encryptedData, utf8EncodeAndEncrypt(serializedKey));
|
||||||
|
encryptedData.push(randomGenerator() ^ 58);
|
||||||
|
Array.prototype.push.apply(encryptedData, utf8EncodeAndEncrypt(value));
|
||||||
|
|
||||||
|
// 第一次调用时添加额外数据
|
||||||
|
if (isFirstCall) {
|
||||||
|
isFirstCall = false;
|
||||||
|
// var hsv = typeof window._hsv == "string" && window._hsv.length > 0 ||
|
||||||
|
// typeof window._hsv == "number" && !isNaN(window._hsv) ? window._hsv : 33;
|
||||||
|
//todo hsv 怎么拿到
|
||||||
|
var hsv = 33;
|
||||||
|
collectAndEncrypt("r3n", hsv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var processedKeys = new Set();
|
||||||
|
|
||||||
|
// 返回三个函数的数组
|
||||||
|
return [
|
||||||
|
// 1. 主要的数据收集函数
|
||||||
|
collectAndEncrypt,
|
||||||
|
|
||||||
|
// 2. 带去重保护的数据收集函数
|
||||||
|
function (key, value) {
|
||||||
|
if (!processedKeys.has(key)) {
|
||||||
|
processedKeys.add(key);
|
||||||
|
collectAndEncrypt(key, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 3. 最终生成加密字符串的函数
|
||||||
|
function (finalKey) {
|
||||||
|
var finalRandomGen = createRandomGenerator(djb2HashString(finalKey) ^ 1809053797, timeBasedRandom);
|
||||||
|
collectAndEncrypt("bpc", ++callCounter);
|
||||||
|
|
||||||
|
// 对收集的数据进行最终加密
|
||||||
|
var finalEncrypted = [];
|
||||||
|
for (var i = 0; i < encryptedData.length; i++) {
|
||||||
|
finalEncrypted.push(encryptedData[i] ^ finalRandomGen());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加结束标记
|
||||||
|
finalEncrypted.push(randomGenerator(true) ^ 125 ^ finalRandomGen());
|
||||||
|
|
||||||
|
// Base64编码
|
||||||
|
var resultBytes = finalEncrypted;
|
||||||
|
var resultChars = [];
|
||||||
|
var baseValue = timeBasedRandom;
|
||||||
|
|
||||||
|
for (var pos = 0; pos < resultBytes.length;) {
|
||||||
|
var triplet = (--baseValue & 255 ^ resultBytes[pos++]) << 16 |
|
||||||
|
(--baseValue & 255 ^ resultBytes[pos++]) << 8 |
|
||||||
|
--baseValue & 255 ^ resultBytes[pos++];
|
||||||
|
|
||||||
|
resultChars.push(String.fromCharCode(mapChar(triplet >> 18 & 63)));
|
||||||
|
resultChars.push(String.fromCharCode(mapChar(triplet >> 12 & 63)));
|
||||||
|
resultChars.push(String.fromCharCode(mapChar(triplet >> 6 & 63)));
|
||||||
|
resultChars.push(String.fromCharCode(mapChar(triplet & 63)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理剩余字节
|
||||||
|
if (resultBytes.length % 3) {
|
||||||
|
resultChars.length -= 3 - (resultBytes.length % 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultChars.join("");
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 创建一个工具函数来生成JSPL
|
||||||
|
function createJSPLGenerator(dateTimeStamp) {
|
||||||
|
return {
|
||||||
|
generate: function (sessionData, fingerprintData) {
|
||||||
|
// let dateTimeStamp = Math.floor(Date.now() / 1000)
|
||||||
|
// console.log("dateTimeStamp : " + dateTimeStamp)
|
||||||
|
// 使用会话数据作为输入
|
||||||
|
var functions = zn(sessionData, dateTimeStamp);
|
||||||
|
var collect = functions[1]; // 收集函数
|
||||||
|
var generate = functions[2]; //加密函数
|
||||||
|
|
||||||
|
// 收集指纹数据
|
||||||
|
for (var key in fingerprintData) {
|
||||||
|
if (fingerprintData.hasOwnProperty(key)) {
|
||||||
|
collect(key, fingerprintData[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成并返回JSPL
|
||||||
|
return generate("jspl");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// print process.argv
|
||||||
|
// process.argv.forEach(function (val, index, array) {
|
||||||
|
// console.log(index + ': ' + val);
|
||||||
|
// });
|
||||||
|
var fingerprint = undefined
|
||||||
|
var dateTimeStamp = undefined
|
||||||
|
if (process.argv[2] != undefined) {
|
||||||
|
fingerprint = JSON.parse(process.argv[2])
|
||||||
|
}
|
||||||
|
dateTimeStamp = JSON.parse(process.argv[3])
|
||||||
|
|
||||||
|
// main
|
||||||
|
// if (dateTimeStamp == undefined) {
|
||||||
|
// dateTimeStamp = Math.floor(Date.now() / 1000)
|
||||||
|
// }
|
||||||
|
// let dateTimeStamp = Math.floor(Date.now() / 1000)
|
||||||
|
// let dateTimeStamp = 1754908260
|
||||||
|
|
||||||
|
// console.log(dateTimeStamp)
|
||||||
|
// 使用示例
|
||||||
|
let jsplGenerator = createJSPLGenerator(dateTimeStamp);
|
||||||
|
|
||||||
|
// fingerprint = {
|
||||||
|
// "nddc": 1,
|
||||||
|
// "r3n": 33,
|
||||||
|
// "exp8": 0,
|
||||||
|
// "uid": null,
|
||||||
|
// "bci": true,
|
||||||
|
// "bcl": 0.8,
|
||||||
|
// "bct": "Infinity",
|
||||||
|
// "bdt": "Infinity",
|
||||||
|
// "dp0": false,
|
||||||
|
// "ucdv": false,
|
||||||
|
// "wdifrm": false,
|
||||||
|
// "iwgl": "undefined",
|
||||||
|
// "npmtm": false,
|
||||||
|
// "wdif": false,
|
||||||
|
// "wdifpnh": 2800984568,
|
||||||
|
// "lg": "fr-FR",
|
||||||
|
// "isb": false,
|
||||||
|
// "idp": true,
|
||||||
|
// "crt": 0,
|
||||||
|
// "vnd": "Google Inc.",
|
||||||
|
// "bid": "NA",
|
||||||
|
// "med": "defined",
|
||||||
|
// "pltod": false,
|
||||||
|
// "csssp": "",
|
||||||
|
// "awe": false,
|
||||||
|
// "phe": false,
|
||||||
|
// "dat": false,
|
||||||
|
// "nm": false,
|
||||||
|
// "geb": false,
|
||||||
|
// "sqt": false,
|
||||||
|
// "pf": "Linux armv81",
|
||||||
|
// "hc": 8,
|
||||||
|
// "br_oh": 745,
|
||||||
|
// "br_ow": 393,
|
||||||
|
// "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36",
|
||||||
|
// "wbd": false,
|
||||||
|
// "ts_mtp": 5,
|
||||||
|
// "mob": true,
|
||||||
|
// "lgs": ["fr-FR", "en-US", "zh-CN", "zh", "fr", "en"],
|
||||||
|
// "dvm": 4,
|
||||||
|
// "hcovdr": false,
|
||||||
|
// "plovdr": false,
|
||||||
|
// "ftsovdr": false,
|
||||||
|
// "orf": "debug",
|
||||||
|
// "ttst": 0.8517814137469564,
|
||||||
|
// "br_w": 393,
|
||||||
|
// "br_h": 745,
|
||||||
|
// "br_iw": 513,
|
||||||
|
// "br_ih": 847,
|
||||||
|
// "ars_w": 393,
|
||||||
|
// "ars_h": 876,
|
||||||
|
// "rs_w": 393,
|
||||||
|
// "rs_h": 876,
|
||||||
|
// "rs_cd": 24,
|
||||||
|
// "pr": 2.75,
|
||||||
|
// "so": "portrait-primary",
|
||||||
|
// "vco": "",
|
||||||
|
// "vcots": false,
|
||||||
|
// "vch": "probably",
|
||||||
|
// "vchts": true,
|
||||||
|
// "vcw": "probably",
|
||||||
|
// "vcwts": true,
|
||||||
|
// "vc3": "maybe",
|
||||||
|
// "vc3ts": false,
|
||||||
|
// "vcmp": "",
|
||||||
|
// "vcmpts": false,
|
||||||
|
// "vcq": "",
|
||||||
|
// "vcqts": false,
|
||||||
|
// "vc1": "probably",
|
||||||
|
// "vc1ts": true,
|
||||||
|
// "cssS": "3.93,0.39,12.24,9.75,5.64,10.98,13.46,2.19,6.51",
|
||||||
|
// "css0": "7,75,0",
|
||||||
|
// "css1": "9.74354,0.354646,-0.00922698,0.000685511,0.203723,-5.61673,-0.755238,0.0561098,-0.0279023,0.481607,-10.9534,0.813777,-0.375565,6.48243,-147.433,11.9534",
|
||||||
|
// "cssH": "15px",
|
||||||
|
// "plu": "",
|
||||||
|
// "plgod": false,
|
||||||
|
// "plg": 0,
|
||||||
|
// "plgne": "NA",
|
||||||
|
// "plgre": "NA",
|
||||||
|
// "plgof": "NA",
|
||||||
|
// "plggt": "NA",
|
||||||
|
// "mmt": "empty",
|
||||||
|
// "bchk": "3223aeb6721e0d0917e792818d193ac88dcd62fad5cad7bf7a2b2b473ecf58ee60f018dbdb1a5832e8dc6528387b0745971dbcd82387261e9a4e3f",
|
||||||
|
// "nt_tcp": 0,
|
||||||
|
// "nt_dns": 0,
|
||||||
|
// "nt_rd": 0,
|
||||||
|
// "nt_irt": -29.600000008940697,
|
||||||
|
// "nt_rt": 1129,
|
||||||
|
// "nt_tls": 6.700000002980232,
|
||||||
|
// "nt_ttf": 1166.7000000029802,
|
||||||
|
// "nt_swt": 22.900000005960464,
|
||||||
|
// "nt_csd": 411060,
|
||||||
|
// "nt_nhp": "h2",
|
||||||
|
// "nt_rdc": 0,
|
||||||
|
// "nt_it": "navigation",
|
||||||
|
// "nt_prs": 6.700000002980232,
|
||||||
|
// "nt_esc": 0,
|
||||||
|
// "nt_ttrd": null,
|
||||||
|
// "nt_le": 3.0999999940395355,
|
||||||
|
// "nt_dcle": 141.79999999701977,
|
||||||
|
// "nt_di": 1415.2000000029802,
|
||||||
|
// "nt_dc": 2862.6000000089407,
|
||||||
|
// "ccsT": "Error\nat S (https://d.digital.hermes/tags.js:173:22)\nat Un.C (https://d.digital.hermes/tags.js:1252:41\nat nrWrapper (https://www.hermes.com/fr/fr/category/femme/Sacs%20et%20pochettes%20pour%20Femme%20_%20Herme%CC%80s%20France.html:7:13468)",
|
||||||
|
// "ccsB": "tags.js:1252:41 at nrWrapper (sac-p-tit-arcon-H085871CKAO.html:7:13468)",
|
||||||
|
// "ccsH": "1050544242",
|
||||||
|
// "ccsV": ",993b46baf0942a343b7e6b02fa3f8eef64727f077d3b0055af56e6994dcaf046",
|
||||||
|
// "muev": false,
|
||||||
|
// "pro_t": true,
|
||||||
|
// "wglo": true,
|
||||||
|
// "prso": true,
|
||||||
|
// "wbst": true,
|
||||||
|
// "psn": true,
|
||||||
|
// "edp": true,
|
||||||
|
// "addt": true,
|
||||||
|
// "wsdc": true,
|
||||||
|
// "ccsr": true,
|
||||||
|
// "nuad": true,
|
||||||
|
// "bcda": true,
|
||||||
|
// "idn": true,
|
||||||
|
// "capi": true,
|
||||||
|
// "svde": true,
|
||||||
|
// "vpbq": true,
|
||||||
|
// "mq": "aptr:coarse,ahvr:none",
|
||||||
|
// "aco": "probably",
|
||||||
|
// "acots": false,
|
||||||
|
// "acmp": "probably",
|
||||||
|
// "acmpts": true,
|
||||||
|
// "acw": "probably",
|
||||||
|
// "acwts": false,
|
||||||
|
// "acma": "maybe",
|
||||||
|
// "acmats": false,
|
||||||
|
// "acaa": "probably",
|
||||||
|
// "acaats": true,
|
||||||
|
// "ac3": "",
|
||||||
|
// "ac3ts": false,
|
||||||
|
// "acf": "probably",
|
||||||
|
// "acfts": false,
|
||||||
|
// "acmp4": "maybe",
|
||||||
|
// "acmp4ts": false,
|
||||||
|
// "acmp3": "probably",
|
||||||
|
// "acmp3ts": false,
|
||||||
|
// "acwm": "maybe",
|
||||||
|
// "acwmts": false,
|
||||||
|
// "ocpt": false,
|
||||||
|
// "ckwa": false,
|
||||||
|
// "spwn": false,
|
||||||
|
// "emt": false,
|
||||||
|
// "bfr": false,
|
||||||
|
// "tz": -120,
|
||||||
|
// "hdn": false,
|
||||||
|
// "xt1": false,
|
||||||
|
// "cdhf": false,
|
||||||
|
// "eva": 33,
|
||||||
|
// "cokys": ",loadTimes,csi,app",
|
||||||
|
// "ecpc": false,
|
||||||
|
// "nhi": ",64,true,Nexus 5,Android,6.0,138.0.7204.184,false",
|
||||||
|
// "k_lyts": 48,
|
||||||
|
// "k_lytk": "kg20va`l\u00a7'w8mh.71pdfoqcn[zy365x/\\,-4bt9siu=j;r]e",
|
||||||
|
// "emd": "k:ai,vi,ao",
|
||||||
|
// "wwl": false,
|
||||||
|
// "glvd": "Google Inc. (ARM)",
|
||||||
|
// "glrd": "ANGLE (ARM,Mali-G57 MC2,OpenGL ES 3.2)",
|
||||||
|
// "tzp": "Europe/Paris",
|
||||||
|
// "isf": true,
|
||||||
|
// "isf2": false,
|
||||||
|
// "dt": false,
|
||||||
|
// "fph": 416468867,
|
||||||
|
// "jset": 1754750959
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.log("received fringerprint is :"+fingerprint)
|
||||||
|
// console.log(fingerprint)
|
||||||
|
let jspl = jsplGenerator.generate("user_session_12345", fingerprint);
|
||||||
|
console.log(jspl);
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def encrpte_to_jspl(fingerprint_json):
|
||||||
|
dir = os.path.dirname(__file__)
|
||||||
|
# print(fingerprint_json)
|
||||||
|
_timestamp = int(time.time())
|
||||||
|
p = subprocess.Popen(['node', dir + '/jspl_encoder.js', fingerprint_json, str(_timestamp)], stdout=subprocess.PIPE)
|
||||||
|
encrypted_fingerprint = p.stdout.read()
|
||||||
|
_result = encrypted_fingerprint.decode('utf-8')
|
||||||
|
return _result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
fingerprint = {
|
||||||
|
"nddc": 1,
|
||||||
|
"r3n": 33,
|
||||||
|
"exp8": 0,
|
||||||
|
"uid": None,
|
||||||
|
"bci": True,
|
||||||
|
"bcl": 0.8,
|
||||||
|
"bct": "Infinity",
|
||||||
|
"bdt": "Infinity",
|
||||||
|
"dp0": False,
|
||||||
|
"ucdv": False,
|
||||||
|
"wdifrm": False,
|
||||||
|
"iwgl": "undefined",
|
||||||
|
"npmtm": False,
|
||||||
|
"wdif": False,
|
||||||
|
"wdifpnh": 2800984568,
|
||||||
|
"lg": "fr-FR",
|
||||||
|
"isb": False,
|
||||||
|
"idp": True,
|
||||||
|
"crt": 0,
|
||||||
|
"vnd": "Google Inc.",
|
||||||
|
"bid": "NA",
|
||||||
|
"med": "defined",
|
||||||
|
"pltod": False,
|
||||||
|
"csssp": "",
|
||||||
|
"awe": False,
|
||||||
|
"phe": False,
|
||||||
|
"dat": False,
|
||||||
|
"nm": False,
|
||||||
|
"geb": False,
|
||||||
|
"sqt": False,
|
||||||
|
"pf": "Linux armv81",
|
||||||
|
"hc": 8,
|
||||||
|
"br_oh": 745,
|
||||||
|
"br_ow": 393,
|
||||||
|
"ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36",
|
||||||
|
"wbd": False,
|
||||||
|
"ts_mtp": 5,
|
||||||
|
"mob": True,
|
||||||
|
"lgs": ["fr-FR", "en-US", "zh-CN", "zh", "fr", "en"],
|
||||||
|
"dvm": 4,
|
||||||
|
"hcovdr": False,
|
||||||
|
"plovdr": False,
|
||||||
|
"ftsovdr": False,
|
||||||
|
"orf": "debug",
|
||||||
|
"ttst": 0.8517814137469564,
|
||||||
|
"br_w": 393,
|
||||||
|
"br_h": 745,
|
||||||
|
"br_iw": 513,
|
||||||
|
"br_ih": 847,
|
||||||
|
"ars_w": 393,
|
||||||
|
"ars_h": 876,
|
||||||
|
"rs_w": 393,
|
||||||
|
"rs_h": 876,
|
||||||
|
"rs_cd": 24,
|
||||||
|
"pr": 2.75,
|
||||||
|
"so": "portrait-primary",
|
||||||
|
"vco": "",
|
||||||
|
"vcots": False,
|
||||||
|
"vch": "probably",
|
||||||
|
"vchts": True,
|
||||||
|
"vcw": "probably",
|
||||||
|
"vcwts": True,
|
||||||
|
"vc3": "maybe",
|
||||||
|
"vc3ts": False,
|
||||||
|
"vcmp": "",
|
||||||
|
"vcmpts": False,
|
||||||
|
"vcq": "",
|
||||||
|
"vcqts": False,
|
||||||
|
"vc1": "probably",
|
||||||
|
"vc1ts": True,
|
||||||
|
"cssS": "3.93,0.39,12.24,9.75,5.64,10.98,13.46,2.19,6.51",
|
||||||
|
"css0": "7,75,0",
|
||||||
|
"css1": "9.74354,0.354646,-0.00922698,0.000685511,0.203723,-5.61673,-0.755238,0.0561098,-0.0279023,0.481607,-10.9534,0.813777,-0.375565,6.48243,-147.433,11.9534",
|
||||||
|
"cssH": "15px",
|
||||||
|
"plu": "",
|
||||||
|
"plgod": False,
|
||||||
|
"plg": 0,
|
||||||
|
"plgne": "NA",
|
||||||
|
"plgre": "NA",
|
||||||
|
"plgof": "NA",
|
||||||
|
"plggt": "NA",
|
||||||
|
"mmt": "empty",
|
||||||
|
"bchk": "3223aeb6721e0d0917e792818d193ac88dcd62fad5cad7bf7a2b2b473ecf58ee60f018dbdb1a5832e8dc6528387b0745971dbcd82387261e9a4e3f",
|
||||||
|
"nt_tcp": 0,
|
||||||
|
"nt_dns": 0,
|
||||||
|
"nt_rd": 0,
|
||||||
|
"nt_irt": -29.600000008940697,
|
||||||
|
"nt_rt": 1129,
|
||||||
|
"nt_tls": 6.700000002980232,
|
||||||
|
"nt_ttf": 1166.7000000029802,
|
||||||
|
"nt_swt": 22.900000005960464,
|
||||||
|
"nt_csd": 411060,
|
||||||
|
"nt_nhp": "h2",
|
||||||
|
"nt_rdc": 0,
|
||||||
|
"nt_it": "navigation",
|
||||||
|
"nt_prs": 6.700000002980232,
|
||||||
|
"nt_esc": 0,
|
||||||
|
"nt_ttrd": None,
|
||||||
|
"nt_le": 3.0999999940395355,
|
||||||
|
"nt_dcle": 141.79999999701977,
|
||||||
|
"nt_di": 1415.2000000029802,
|
||||||
|
"nt_dc": 2862.6000000089407,
|
||||||
|
"ccsT": "Error\nat S (https://d.digital.hermes/tags.js:173:22)\nat Un.C (https://d.digital.hermes/tags.js:1252:41\nat nrWrapper (https://www.hermes.com/fr/fr/category/femme/Sacs%20et%20pochettes%20pour%20Femme%20_%20Herme%CC%80s%20France.html:7:13468)",
|
||||||
|
"ccsB": "tags.js:1252:41 at nrWrapper (sac-p-tit-arcon-H085871CKAO.html:7:13468)",
|
||||||
|
"ccsH": "1050544242",
|
||||||
|
"ccsV": ",993b46baf0942a343b7e6b02fa3f8eef64727f077d3b0055af56e6994dcaf046",
|
||||||
|
"muev": False,
|
||||||
|
"pro_t": True,
|
||||||
|
"wglo": True,
|
||||||
|
"prso": True,
|
||||||
|
"wbst": True,
|
||||||
|
"psn": True,
|
||||||
|
"edp": True,
|
||||||
|
"addt": True,
|
||||||
|
"wsdc": True,
|
||||||
|
"ccsr": True,
|
||||||
|
"nuad": True,
|
||||||
|
"bcda": True,
|
||||||
|
"idn": True,
|
||||||
|
"capi": True,
|
||||||
|
"svde": True,
|
||||||
|
"vpbq": True,
|
||||||
|
"mq": "aptr:coarse,ahvr:none",
|
||||||
|
"aco": "probably",
|
||||||
|
"acots": False,
|
||||||
|
"acmp": "probably",
|
||||||
|
"acmpts": True,
|
||||||
|
"acw": "probably",
|
||||||
|
"acwts": False,
|
||||||
|
"acma": "maybe",
|
||||||
|
"acmats": False,
|
||||||
|
"acaa": "probably",
|
||||||
|
"acaats": True,
|
||||||
|
"ac3": "",
|
||||||
|
"ac3ts": False,
|
||||||
|
"acf": "probably",
|
||||||
|
"acfts": False,
|
||||||
|
"acmp4": "maybe",
|
||||||
|
"acmp4ts": False,
|
||||||
|
"acmp3": "probably",
|
||||||
|
"acmp3ts": False,
|
||||||
|
"acwm": "maybe",
|
||||||
|
"acwmts": False,
|
||||||
|
"ocpt": False,
|
||||||
|
"ckwa": False,
|
||||||
|
"spwn": False,
|
||||||
|
"emt": False,
|
||||||
|
"bfr": False,
|
||||||
|
"tz": -120,
|
||||||
|
"hdn": False,
|
||||||
|
"xt1": False,
|
||||||
|
"cdhf": False,
|
||||||
|
"eva": 33,
|
||||||
|
"cokys": ",loadTimes,csi,app",
|
||||||
|
"ecpc": False,
|
||||||
|
"nhi": ",64,true,Nexus 5,Android,6.0,138.0.7204.184,false",
|
||||||
|
"k_lyts": 48,
|
||||||
|
"k_lytk": "kg20va`l\u00a7'w8mh.71pdfoqcn[zy365x/\\,-4bt9siu=j;r]e",
|
||||||
|
"emd": "k:ai,vi,ao",
|
||||||
|
"wwl": False,
|
||||||
|
"glvd": "Google Inc. (ARM)",
|
||||||
|
"glrd": "ANGLE (ARM,Mali-G57 MC2,OpenGL ES 3.2)",
|
||||||
|
"tzp": "Europe/Paris",
|
||||||
|
"isf": True,
|
||||||
|
"isf2": False,
|
||||||
|
"dt": False,
|
||||||
|
"fph": 416468867,
|
||||||
|
"jset": 1754750959
|
||||||
|
}
|
||||||
|
fingerprint_json = json.dumps(fingerprint)
|
||||||
|
print(encrpte_to_jspl(fingerprint_json))
|
||||||
+103
-36
@@ -1,6 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ from models.contact_pojo import ContactPojo
|
|||||||
from models.mail_pojo import MailAddress
|
from models.mail_pojo import MailAddress
|
||||||
from models.regisered_user_pojo import RegisteredUserPojo
|
from models.regisered_user_pojo import RegisteredUserPojo
|
||||||
|
|
||||||
MONGO_DB_URL = "mongodb://mongo.lpaconsulting.fr/?timeoutMS=100000"
|
MONGO_DB_URL = "mongodb://mongo2.lpaconsulting.fr/?timeoutMS=100000"
|
||||||
CAPTCHA_ERROR_COLLECTION_PREFIX = "CAPTCHA_ERROR_"
|
CAPTCHA_ERROR_COLLECTION_PREFIX = "CAPTCHA_ERROR_"
|
||||||
BLACK_LIST = "BLACK_LIST"
|
BLACK_LIST = "BLACK_LIST"
|
||||||
ACCEPTED_APPOINTMENT_LIST = "ACCEPTED_APPOINTMENT_LIST"
|
ACCEPTED_APPOINTMENT_LIST = "ACCEPTED_APPOINTMENT_LIST"
|
||||||
@@ -25,7 +26,22 @@ CONTACT_LIST_SERIAL_MAP = "CONTACT_LIST_SERIAL_MAP"
|
|||||||
|
|
||||||
class MongoDbManager:
|
class MongoDbManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
client = MongoClient(MONGO_DB_URL, username='appointment', password='Rdv@2022', authSource='appointment')
|
# Get username and password from environment variables
|
||||||
|
mongo_username = os.getenv("MONGO_USERNAME")
|
||||||
|
mongo_password = os.getenv("MONGO_PASSWORD")
|
||||||
|
|
||||||
|
# Validate that environment variables exist
|
||||||
|
if not mongo_username or not mongo_password:
|
||||||
|
raise ValueError(
|
||||||
|
"MONGO_USERNAME and MONGO_PASSWORD environment variables must be set"
|
||||||
|
)
|
||||||
|
|
||||||
|
client = MongoClient(
|
||||||
|
MONGO_DB_URL,
|
||||||
|
username=mongo_username,
|
||||||
|
password=mongo_password,
|
||||||
|
authSource="appointment",
|
||||||
|
)
|
||||||
self.db = client.appointment
|
self.db = client.appointment
|
||||||
self.logger = logging.getLogger("mongoDb")
|
self.logger = logging.getLogger("mongoDb")
|
||||||
|
|
||||||
@@ -36,8 +52,13 @@ class MongoDbManager:
|
|||||||
def insert_reserve_result(self, collection_name, reserve: ReserveResultPojo):
|
def insert_reserve_result(self, collection_name, reserve: ReserveResultPojo):
|
||||||
try:
|
try:
|
||||||
collection_to_use = self.db[collection_name]
|
collection_to_use = self.db[collection_name]
|
||||||
collection_to_use.replace_one(filter={'_id': reserve.id, }, replacement=reserve.to_firestore_dict(),
|
collection_to_use.replace_one(
|
||||||
upsert=True)
|
filter={
|
||||||
|
"_id": reserve.id,
|
||||||
|
},
|
||||||
|
replacement=reserve.to_firestore_dict(),
|
||||||
|
upsert=True,
|
||||||
|
)
|
||||||
except Exception as Error:
|
except Exception as Error:
|
||||||
self.logger.info(Error)
|
self.logger.info(Error)
|
||||||
|
|
||||||
@@ -83,7 +104,14 @@ class MongoDbManager:
|
|||||||
result_list.append(ContactPojo.from_firestore_dict(document))
|
result_list.append(ContactPojo.from_firestore_dict(document))
|
||||||
return result_list
|
return result_list
|
||||||
|
|
||||||
def save_links_to_validate(self, link: str, mail_address: str, model: str, _all_contact_list: list):
|
def save_links_to_validate(
|
||||||
|
self,
|
||||||
|
link: str,
|
||||||
|
mail_address: str,
|
||||||
|
model: str,
|
||||||
|
_all_contact_list: list,
|
||||||
|
_used_ip: str = "",
|
||||||
|
):
|
||||||
collection_to_use = self.db[LINKS_TO_VALIDATE]
|
collection_to_use = self.db[LINKS_TO_VALIDATE]
|
||||||
updated_at = time.strftime("%H:%M:%S", time.localtime())
|
updated_at = time.strftime("%H:%M:%S", time.localtime())
|
||||||
_ip_country = "FR"
|
_ip_country = "FR"
|
||||||
@@ -94,30 +122,42 @@ class MongoDbManager:
|
|||||||
_ip_country = _contact.ip_country
|
_ip_country = _contact.ip_country
|
||||||
|
|
||||||
if len(mail_address) > 0:
|
if len(mail_address) > 0:
|
||||||
collection_to_use.replace_one(filter={'_id': mail_address, }, replacement={
|
collection_to_use.replace_one(
|
||||||
u'url': link,
|
filter={
|
||||||
u'email': mail_address,
|
"_id": mail_address,
|
||||||
u'serial': serial,
|
},
|
||||||
u'model': model,
|
replacement={
|
||||||
u'ip_country': _ip_country,
|
"url": link,
|
||||||
"updated_at": updated_at
|
"email": mail_address,
|
||||||
},
|
"serial": serial,
|
||||||
upsert=True)
|
"model": model,
|
||||||
|
"ip_country": _ip_country,
|
||||||
|
"_used_ip": _used_ip,
|
||||||
|
"updated_at": updated_at,
|
||||||
|
},
|
||||||
|
upsert=True,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
collection_to_use.replace_one(filter={'_id': link, }, replacement={
|
collection_to_use.replace_one(
|
||||||
u'url': link,
|
filter={
|
||||||
u'serial': serial,
|
"_id": link,
|
||||||
u'model': model,
|
},
|
||||||
u'ip_country': _ip_country,
|
replacement={
|
||||||
"updated_at": updated_at
|
"url": link,
|
||||||
},
|
"serial": serial,
|
||||||
upsert=True)
|
"model": model,
|
||||||
|
"ip_country": _ip_country,
|
||||||
|
"_used_ip": _used_ip,
|
||||||
|
"updated_at": updated_at,
|
||||||
|
},
|
||||||
|
upsert=True,
|
||||||
|
)
|
||||||
|
|
||||||
def get_code_for_email(self, email: str):
|
def get_code_for_email(self, email: str):
|
||||||
collection_name = DESTINATION_EMAIL_LIST
|
collection_name = DESTINATION_EMAIL_LIST
|
||||||
try:
|
try:
|
||||||
collection_to_use = self.db[collection_name]
|
collection_to_use = self.db[collection_name]
|
||||||
mailDocument = collection_to_use.find_one(filter={'_id': email})
|
mailDocument = collection_to_use.find_one(filter={"_id": email})
|
||||||
if mailDocument is not None:
|
if mailDocument is not None:
|
||||||
return MailAddress.from_firestore_dict(mailDocument).password
|
return MailAddress.from_firestore_dict(mailDocument).password
|
||||||
else:
|
else:
|
||||||
@@ -131,7 +171,9 @@ class MongoDbManager:
|
|||||||
_cursor = self.db[_collection_name]
|
_cursor = self.db[_collection_name]
|
||||||
registered_user_list = []
|
registered_user_list = []
|
||||||
for document in _cursor.find():
|
for document in _cursor.find():
|
||||||
registered_user_list.append(RegisteredUserPojo.from_firestore_dict(document))
|
registered_user_list.append(
|
||||||
|
RegisteredUserPojo.from_firestore_dict(document)
|
||||||
|
)
|
||||||
return registered_user_list
|
return registered_user_list
|
||||||
|
|
||||||
def get_destination_emails(self) -> list:
|
def get_destination_emails(self) -> list:
|
||||||
@@ -164,8 +206,20 @@ class MongoDbManager:
|
|||||||
self.logger.info(error)
|
self.logger.info(error)
|
||||||
return link_list
|
return link_list
|
||||||
|
|
||||||
def link_validated_for_result(self, link: str, linkPojo: LinkPojo, state=True, is_duplicated=False,
|
def link_validated_for_result(
|
||||||
is_invalid=False, segement_position=1, ua=""):
|
self,
|
||||||
|
link: str,
|
||||||
|
linkPojo: LinkPojo,
|
||||||
|
state=True,
|
||||||
|
is_duplicated=False,
|
||||||
|
is_invalid=False,
|
||||||
|
segement_position=1,
|
||||||
|
ua="",
|
||||||
|
model="",
|
||||||
|
timestamp_in_s: list = None,
|
||||||
|
):
|
||||||
|
if timestamp_in_s is None:
|
||||||
|
timestamp_in_s = []
|
||||||
print("link_validated_for_result() called with url = " + link)
|
print("link_validated_for_result() called with url = " + link)
|
||||||
if is_duplicated:
|
if is_duplicated:
|
||||||
_id = link.split("/")[-2]
|
_id = link.split("/")[-2]
|
||||||
@@ -176,7 +230,10 @@ class MongoDbManager:
|
|||||||
print("link_validated_for_result() called with id = " + _id)
|
print("link_validated_for_result() called with id = " + _id)
|
||||||
|
|
||||||
collection_name = str(datetime.date.today())
|
collection_name = str(datetime.date.today())
|
||||||
print("link_validated_for_result() called with collection_name = " + collection_name)
|
print(
|
||||||
|
"link_validated_for_result() called with collection_name = "
|
||||||
|
+ collection_name
|
||||||
|
)
|
||||||
|
|
||||||
collection = self.db[collection_name]
|
collection = self.db[collection_name]
|
||||||
validated_at = time.strftime("%H:%M:%S", time.localtime())
|
validated_at = time.strftime("%H:%M:%S", time.localtime())
|
||||||
@@ -185,17 +242,27 @@ class MongoDbManager:
|
|||||||
validated_by = "Invalid"
|
validated_by = "Invalid"
|
||||||
if is_duplicated:
|
if is_duplicated:
|
||||||
validated_by = "Double"
|
validated_by = "Double"
|
||||||
collection.find_one_and_update({'_id': _id}, {
|
collection.find_one_and_update(
|
||||||
"$set": {"url_validated": state, "validated_at": validated_at, "id": _id, "email": linkPojo.email,
|
{"_id": _id},
|
||||||
"url": link,
|
{
|
||||||
"source_from": linkPojo.model,
|
"$set": {
|
||||||
"serial": linkPojo.serial,
|
"url_validated": state,
|
||||||
"validated_by_ua": ua,
|
"validated_at": validated_at,
|
||||||
"validated_by": validated_by}},
|
"id": _id,
|
||||||
upsert=True)
|
"email": linkPojo.email,
|
||||||
|
"url": link,
|
||||||
|
"validated_by_model": model,
|
||||||
|
"serial": linkPojo.serial,
|
||||||
|
"validated_by_ua": ua,
|
||||||
|
"timestamp_in_s": "-".join(str(x) for x in timestamp_in_s),
|
||||||
|
"validated_by": validated_by,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
upsert=True,
|
||||||
|
)
|
||||||
# remove the link from db
|
# remove the link from db
|
||||||
collection_to_use = self.db[LINKS_TO_VALIDATE]
|
collection_to_use = self.db[LINKS_TO_VALIDATE]
|
||||||
collection_to_use.delete_one({'_id': linkPojo.email})
|
collection_to_use.delete_one({"_id": linkPojo.email})
|
||||||
|
|
||||||
|
|
||||||
MONGO_STORE_MANAGER = MongoDbManager()
|
MONGO_STORE_MANAGER = MongoDbManager()
|
||||||
|
|||||||
@@ -0,0 +1,423 @@
|
|||||||
|
nddc:1 --> OK, 1, 需要从网页中获得
|
||||||
|
数值应该就是1
|
||||||
|
var nddc;
|
||||||
|
try {
|
||||||
|
if ((nddc = (document.cookie.match(/datadome=/g) || []).length) > 1) {
|
||||||
|
t.deleteAllDDCookies();
|
||||||
|
}
|
||||||
|
} catch (n) {
|
||||||
|
nddc = "err";
|
||||||
|
}
|
||||||
|
|
||||||
|
r3n:33
|
||||||
|
r3n 数值是window._hsv 的值,如果没有window._hsv的值,那么返回33
|
||||||
|
// var hsv = typeof window._hsv == "string" && window._hsv.length > 0 ||
|
||||||
|
// typeof window._hsv == "number" && !isNaN(window._hsv) ? window._hsv : 33;
|
||||||
|
exp8:0
|
||||||
|
固定是0
|
||||||
|
uid:null
|
||||||
|
默认为null
|
||||||
|
n.i("uid", t.getCookie("correlation_id"));
|
||||||
|
bci:true
|
||||||
|
bcl:0.8
|
||||||
|
bct:Infinity
|
||||||
|
bdt:Infinity
|
||||||
|
window.navigator.getBattery().then(function (batteryManager) {
|
||||||
|
n("bci", batteryManager.charging);
|
||||||
|
n("bcl", batteryManager.level);
|
||||||
|
n("bct", batteryManager.chargingTime);
|
||||||
|
n("bdt", batteryManager.dischargingTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
dp0:false
|
||||||
|
默认为false ?
|
||||||
|
ucdv:false
|
||||||
|
默认为false
|
||||||
|
ucdv = typeof objectToInspect != "undefined" && objectToInspect === "null" && typeof result != "undefined" && !!result)
|
||||||
|
wdifrm:false
|
||||||
|
默认为false
|
||||||
|
iwgl:undefined ??
|
||||||
|
|
||||||
|
|
||||||
|
npmtm:false
|
||||||
|
默认为false
|
||||||
|
wdif:false
|
||||||
|
|
||||||
|
wdifpnh:2800984568
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lg:fr-FR
|
||||||
|
n("lg", window.navigator.language || window.navigator.userLanguage || window.navigator.browserLanguage || window.navigator.systemLanguage || "");
|
||||||
|
isb:false
|
||||||
|
n("isb", !!window.navigator.brave);
|
||||||
|
idp:true
|
||||||
|
crt:0
|
||||||
|
crt = window.navigator.connection && window.navigator.connection['rtt']
|
||||||
|
vnd:Google Inc.
|
||||||
|
OK
|
||||||
|
|
||||||
|
bid:NA
|
||||||
|
OK
|
||||||
|
med:defined
|
||||||
|
OK
|
||||||
|
pltod:false
|
||||||
|
默认为false
|
||||||
|
csssp:
|
||||||
|
为空
|
||||||
|
awe:false
|
||||||
|
默认为false
|
||||||
|
phe:false
|
||||||
|
默认为false
|
||||||
|
dat:false
|
||||||
|
默认为false
|
||||||
|
nm:false
|
||||||
|
默认为false
|
||||||
|
geb:false
|
||||||
|
默认为false
|
||||||
|
sqt:false
|
||||||
|
默认为false
|
||||||
|
pf:MacIntel
|
||||||
|
pf = window.navigator.platform
|
||||||
|
hc:12
|
||||||
|
ok
|
||||||
|
br_oh:847
|
||||||
|
ok
|
||||||
|
br_ow:513
|
||||||
|
ok
|
||||||
|
ua:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36
|
||||||
|
ok
|
||||||
|
wbd:false
|
||||||
|
默认为false
|
||||||
|
ts_mtp:0
|
||||||
|
ok
|
||||||
|
mob:true
|
||||||
|
mob = navigator.userAgentData?navigator.userAgentData.mobile:"NA"
|
||||||
|
lgs:["fr-FR","en-US","zh-CN","zh","fr","en"]
|
||||||
|
lgs=JSON.stringify(navigator.languages)
|
||||||
|
dvm:8
|
||||||
|
ok
|
||||||
|
hcovdr:false
|
||||||
|
默认为false
|
||||||
|
plovdr:false
|
||||||
|
默认为false
|
||||||
|
ftsovdr:false
|
||||||
|
默认为false
|
||||||
|
|
||||||
|
orf:debug
|
||||||
|
|
||||||
|
trrd:0.5678095129748595
|
||||||
|
0到1之间的,小数后面有15位的随机数
|
||||||
|
|
||||||
|
br_w:513
|
||||||
|
ok
|
||||||
|
br_h:847
|
||||||
|
ok
|
||||||
|
br_iw:513
|
||||||
|
window.innerWidth || 0
|
||||||
|
br_ih:847
|
||||||
|
window.innerHeight || 0
|
||||||
|
ars_w:513
|
||||||
|
ok
|
||||||
|
ars_h:847
|
||||||
|
ok
|
||||||
|
rs_w:513
|
||||||
|
ok
|
||||||
|
rs_h:847
|
||||||
|
ok
|
||||||
|
rs_cd:24
|
||||||
|
ok
|
||||||
|
pr:2
|
||||||
|
ok
|
||||||
|
so:portrait-primary
|
||||||
|
ok
|
||||||
|
vco:
|
||||||
|
ok
|
||||||
|
vcots:false
|
||||||
|
ok
|
||||||
|
vch:probably
|
||||||
|
ok
|
||||||
|
vchts:true
|
||||||
|
ok
|
||||||
|
vcw:probably
|
||||||
|
ok
|
||||||
|
vcwts:true
|
||||||
|
ok
|
||||||
|
vc3:maybe
|
||||||
|
ok
|
||||||
|
vc3ts:false
|
||||||
|
ok
|
||||||
|
vcmp:
|
||||||
|
ok
|
||||||
|
vcmpts:false
|
||||||
|
ok
|
||||||
|
vcq:
|
||||||
|
ok
|
||||||
|
vcqts:false
|
||||||
|
ok
|
||||||
|
vc1:probably
|
||||||
|
ok
|
||||||
|
vc1ts:true
|
||||||
|
ok
|
||||||
|
|
||||||
|
cssS:3.93,0.39,12.24,9.75,5.64,10.98,13.46,2.19,6.51
|
||||||
|
|
||||||
|
css0:7, 75, 0
|
||||||
|
|
||||||
|
css1:9.74354, 0.354646, -0.00922698, 0.000685511, 0.203723, -5.61673, -0.755238, 0.0561098, -0.0279023, 0.481607, -10.9534, 0.813777, -0.375565, 6.48243, -147.433, 11.9534
|
||||||
|
|
||||||
|
cssH:15px
|
||||||
|
|
||||||
|
plu:empty
|
||||||
|
plgod:false
|
||||||
|
plg:0
|
||||||
|
plgne:NA
|
||||||
|
plgre:NA
|
||||||
|
plgof:NA
|
||||||
|
plggt:NA
|
||||||
|
|
||||||
|
mmt:empty
|
||||||
|
bchk:3223aeb6721e0d0917e7928181193ac88dcd62fad5cadfbe7a2b2b473ecf58ee70f098dbdb1a1832e8dc6528387b0745971dbcd82384261e9a4e3f
|
||||||
|
for (var n = ["AppBannerPromptResult", "webkitRTCPeerConnection", "webkitAudioContext", "webkitRequestAnimationFrame", "chrome.runtime",
|
||||||
|
"chrome.webstore", "console.context", "InputMethodContext", "SVGAnimationElement", "SVGPathSegList", "PasswordCredential", "ViewTransition",
|
||||||
|
"VisualViewport.prototype.segments", "DeprecationReportBody", "MathMLElement", "opr", "CSS2Properties.prototype.colorScheme", "WebKitCSSMatrix",
|
||||||
|
"SVGTextPositioningElement", "XMLHttpRequestEventTarget", "TextDecoderStream", "onloadend", "WritableStream", "TransformStream", "TextTrackCue",
|
||||||
|
"-283", "VisualViewport", "StyleSheet", "RTCDtlsTransport", "Atomics", "StaticRange", "UIEvent", "VideoStreamTrack", "OfflineResourceList",
|
||||||
|
"SVGGeometryElement", "RTCDataChannel", "VTTRegion", "AbortController", "Controllers", "onanimationcancel",
|
||||||
|
"SVGDocument", "IIRFilterNode", "RTCStatsReport", "MediaStreamTrack", "CSS2Properties.prototype.MozOsxFontSmoothing", "CropTarget", "BatteryManager",
|
||||||
|
"LaunchQueue", "CSSFontPaletteValuesRule", "PushSubscriptionOptions", "DOMSettableTokenList", "RTCTrackEvent",
|
||||||
|
"MozSmsMessage", "ServiceWorkerContainer", "CanvasCaptureMediaStream", "DeviceStorage", "XPathNSResolver", "SmartCardEvent", "WeakSet",
|
||||||
|
"MozMobileMessageManager", "External.prototype.getHostEnvironmentValue", "WindowUtils", "XPathNamespace", "SVGFEDropShadowElement",
|
||||||
|
"SharedWorker", "WorkerMessageEvent", "CSS2Properties.prototype.MozOSXFontSmoothing", "AudioSinkInfo",
|
||||||
|
"Notification.prototype.image", "ContentVisibilityAutoStateChangeEvent", "PerformanceResourceTiming.prototype.renderBlockingStatus", "console.createTask",
|
||||||
|
"PerformanceServerTiming", "CanvasFilter", "structuredClone", "onslotchange", "EyeDropper", "URLPattern", "VideoFrame", "WritableStreamDefaultController", "SharedArrayBuffer",
|
||||||
|
"CSSCounterStyleRule", "CustomStateSet", "ReadableStreamDefaultController", "XMLDocument.prototype.hasStorageAccess",
|
||||||
|
"CryptoKey", "SubmitEvent", "MediaMetadata", "VideoPlaybackQuality", "ReadableStreamDefaultReader", "UserActivation", "FragmentDirective",
|
||||||
|
"WebKitMediaKeyError", "RTCRtpTransceiver.prototype.stop", "Scheduling", "EventCounts", "VideoTrackList", "SourceBuffer", "RTCError", "FontFaceSet", "CSSCharsetRule", "MediaDeviceInfo",
|
||||||
|
"RTCPeerConnectionIceErrorEvent", "RTCSctpTransport", "MediaSessionCoordinator", "XULPopupElement", "MediaSourceHandle",
|
||||||
|
"366", "__REACT_DEVTOOLS_GLOBAL_HOOK__", "ShadowRealm", "HTMLSlotElement", "DetachedViewControlEvent", "GeolocationPosition", "SiteBoundCredential", "MediaSource",
|
||||||
|
"WebTransport", "GPUSupportedLimits", "ToggleEvent"], bchk = "", e = 0; e < n.length; e++) {
|
||||||
|
for (var r = n[e].split("."), i = window, o = true, c = 0; c < r.length; c++) {
|
||||||
|
var s = r[c];
|
||||||
|
if (!(s in i)) {
|
||||||
|
o = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c < r.length - 1) {
|
||||||
|
i = i[s];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (o) {
|
||||||
|
bchk += "52738db37a1ea50137e79e8181193ac872cd325ba5cacfbe7aab5b36b9c9879e7c0018dbd31a1832a8dc6528387b67451719dcd8b784a518904e3f07c69b9d30"[e];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
bchk += "3829ae9642df0d791e41d2159da28bd18d056afadf1bd70fc9222a473eaf58e860ff950e7bf35b66e4aa90b156c80c96913dbd9c23c7262e4adbc3ddd77ff263"[e];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nt_tcp:0
|
||||||
|
var performanceNavigationTiming = window.performance.getEntriesByType && window.performance.getEntriesByType("navigation")[0];
|
||||||
|
nt_tcp = performanceNavigationTiming.connectEnd - performanceNavigationTiming.connectStart
|
||||||
|
nt_dns:0
|
||||||
|
|
||||||
|
nt_dns =performanceNavigationTiming.domainLookupEnd - performanceNavigationTiming.domainLookupStart
|
||||||
|
nt_rd:0
|
||||||
|
nt_rd = performanceNavigationTiming.redirectEnd - performanceNavigationTiming.redirectStart
|
||||||
|
nt_irt:-3.400000002235174
|
||||||
|
nt_irt = performanceNavigationTiming.firstInterimResponseStart - performanceNavigationTiming.requestStart
|
||||||
|
nt_rt:0
|
||||||
|
nt_rt = performanceNavigationTiming.responseStart - performanceNavigationTiming.requestStart
|
||||||
|
nt_tls:3.400000002235174
|
||||||
|
let nt_tls = performanceNavigationTiming.requestStart - performanceNavigationTiming.secureConnectionStart
|
||||||
|
nt_ttf:3.099999997764826
|
||||||
|
let nt_ttf = performanceNavigationTiming.responseEnd - performanceNavigationTiming.fetchStart
|
||||||
|
nt_swt:3.400000002235174
|
||||||
|
let nt_swt = performanceNavigationTiming.fetchStart - performanceNavigationTiming.workerStart
|
||||||
|
nt_csd:0
|
||||||
|
let nt_csd = performanceNavigationTiming.decodedBodySize - performanceNavigationTiming.encodedBodySize
|
||||||
|
nt_nhp:
|
||||||
|
let nt_nhp = performanceNavigationTiming.nextHopProtocol
|
||||||
|
nt_rdc:0
|
||||||
|
let nt_rdc = performanceNavigationTiming.redirectCount
|
||||||
|
nt_it:navigation
|
||||||
|
let nt_it = performanceNavigationTiming.initiatorType
|
||||||
|
nt_prs:0
|
||||||
|
let nt_prs = performanceNavigationTiming.requestStart - performanceNavigationTiming.connectEnd
|
||||||
|
nt_esc:-3.400000002235174
|
||||||
|
let nt_esc = performanceNavigationTiming.secureConnectionStart - performanceNavigationTiming.connectStart
|
||||||
|
nt_ttrd:-2
|
||||||
|
e = performanceNavigationTiming.secureConnectionStart - performanceNavigationTiming.connectStart
|
||||||
|
nt_ttrd = (e - (e = performanceNavigationTiming.connectEnd - performanceNavigationTiming.connectStart)) / e)
|
||||||
|
nt_le:0
|
||||||
|
nt_le = performanceNavigationTiming.loadEventEnd - performanceNavigationTiming.loadEventStart
|
||||||
|
nt_dcle:0.10000000149011612
|
||||||
|
nt_dcle = performanceNavigationTiming.domContentLoadedEventEnd - performanceNavigationTiming.domContentLoadedEventStart
|
||||||
|
nt_di:74.80000000074506
|
||||||
|
nt_di = performanceNavigationTiming.domInteractive
|
||||||
|
nt_dc:0
|
||||||
|
nt_dc = performanceNavigationTiming.domComplete
|
||||||
|
ccsT:Error
|
||||||
|
at S (https://d.digital.hermes/tags.js:173:22)
|
||||||
|
at Un.C (https://d.digital.hermes/tags.js:1252:41
|
||||||
|
ccsB:/tags.js:1252:41
|
||||||
|
at nrWrapper (file:///Users/lpan/Downloads/Sacs%20et%20pochettes%20pour%20Femme%20_%20Herme%CC%80s%20France.html:7:13468)
|
||||||
|
|
||||||
|
ccsH:1050544242
|
||||||
|
|
||||||
|
ccsV:,993b46baf0942a343b7e6b02fa3f8eef64727f077d3b0055af56e6994dcaf046
|
||||||
|
muev:false
|
||||||
|
let muev = !!window.MutationEvent
|
||||||
|
pro_t:true
|
||||||
|
let pro_t = typeof window.Promise != "undefined" && !!window.Promise.try
|
||||||
|
wglo:true
|
||||||
|
!!window.WebGLObject
|
||||||
|
prso:true
|
||||||
|
!!window.PressureObserver
|
||||||
|
wbst:true
|
||||||
|
!!window.WebSocketStream
|
||||||
|
psn:true
|
||||||
|
!!window.PermissionStatus && window.PermissionStatus.prototype.hasOwnProperty('name')
|
||||||
|
edp:true
|
||||||
|
!!window.EyeDropper
|
||||||
|
addt:true
|
||||||
|
!window.AudioData
|
||||||
|
wsdc:true
|
||||||
|
!!window.WritableStreamDefaultController
|
||||||
|
ccsr:true
|
||||||
|
!!window.CSSCounterStyleRule
|
||||||
|
nuad:true
|
||||||
|
!!window.NavigatorUAData
|
||||||
|
bcda:true
|
||||||
|
!!window.BarcodeDetector
|
||||||
|
idn:true
|
||||||
|
!(!window.Intl || !window.Intl.DisplayNames)
|
||||||
|
capi:false
|
||||||
|
capi = !!(window.navigator && window.navigator.contacts && window.navigator[2])
|
||||||
|
svde:false
|
||||||
|
!!window.SVGDiscardElement
|
||||||
|
vpbq:true
|
||||||
|
!!(window.HTMLVideoElement && window.HTMLVideoElement.prototype && window.HTMLVideoElement.prototype.getVideoPlaybackQuality)
|
||||||
|
mq:aptr:coarse, ahvr:none
|
||||||
|
aco:probably
|
||||||
|
var audioElement = document.createElement("audio");
|
||||||
|
aco = audioElement.canPlayType("audio/ogg; codecs=\"vorbis\"")
|
||||||
|
acots:false
|
||||||
|
window.MediaSource.isTypeSupported("audio/ogg; codecs=\"vorbis\"")
|
||||||
|
acmp:probably
|
||||||
|
ok audioElement.canPlayType("audio/mpeg;")
|
||||||
|
acmpts:true
|
||||||
|
ok, default to true
|
||||||
|
acw:probably
|
||||||
|
ok, default to probably
|
||||||
|
acwts:false
|
||||||
|
ok, default to false
|
||||||
|
acma:maybe
|
||||||
|
ok, default to maybe
|
||||||
|
acmats:false
|
||||||
|
ok, default to false
|
||||||
|
|
||||||
|
acaa:probably
|
||||||
|
ok, default to probably
|
||||||
|
|
||||||
|
acaats:true
|
||||||
|
ok, default to true
|
||||||
|
|
||||||
|
ac3:
|
||||||
|
default to empty: ""
|
||||||
|
ac3ts:false
|
||||||
|
ok, default to false
|
||||||
|
|
||||||
|
acf:probably
|
||||||
|
ok, default to probably
|
||||||
|
|
||||||
|
acfts:false
|
||||||
|
ok, default to false
|
||||||
|
|
||||||
|
acmp4:maybe
|
||||||
|
ok, default to maybe
|
||||||
|
|
||||||
|
acmp4ts:false
|
||||||
|
ok, default to false
|
||||||
|
|
||||||
|
acmp3:probably
|
||||||
|
ok, default to probably
|
||||||
|
|
||||||
|
acmp3ts:false
|
||||||
|
ok, default to false
|
||||||
|
|
||||||
|
acwm:maybe
|
||||||
|
ok, default to maybe
|
||||||
|
|
||||||
|
acwmts:false
|
||||||
|
ok, default to false
|
||||||
|
|
||||||
|
ocpt:false
|
||||||
|
ok ocpt = audioElement.canPlayType.toString().indexOf('canPlayType') === -1
|
||||||
|
ckwa:false
|
||||||
|
ok, default to false
|
||||||
|
spwn:false
|
||||||
|
spwn = !!window.spawn
|
||||||
|
emt:false
|
||||||
|
emt = !!window.emit
|
||||||
|
bfr:false
|
||||||
|
bfr = !!window.Buffer
|
||||||
|
tz:-120
|
||||||
|
ok
|
||||||
|
hdn:false
|
||||||
|
hdn = !!window.hidden
|
||||||
|
ok, default to false
|
||||||
|
xt1:false
|
||||||
|
xt1 = !!window.navigator.pdfViewerEnabled
|
||||||
|
cdhf:false
|
||||||
|
cdhf = window.hasFocus()
|
||||||
|
eva:40
|
||||||
|
eva = window.XMLDocument.toString().length
|
||||||
|
cokys:,loadTimes,csi,app
|
||||||
|
default to ",loadTimes,csi,app"
|
||||||
|
var cokys = ""
|
||||||
|
for (var v in window.chrome) {
|
||||||
|
cokys += "," + v;
|
||||||
|
}
|
||||||
|
ecpc:false
|
||||||
|
default to false
|
||||||
|
ecpc = !!window.process
|
||||||
|
|
||||||
|
nhi:,64,true,Nexus 5,Android,6.0,138.0.7204.184,false
|
||||||
|
let results = await window.navigator.userAgentData.getHighEntropyValues(["architecture", "bitness", "model", "platformVersion", "uaFullVersion", "wow64"])
|
||||||
|
for (var e = ["architecture", "bitness", "mobile", "model", "platform", "platformVersion", "uaFullVersion", "wow64"], r = [], i = 0; i < e.length; i++) {
|
||||||
|
r.push(results[e[i]]);
|
||||||
|
}
|
||||||
|
nhi = r.join(",");
|
||||||
|
k_lyts:48
|
||||||
|
k_lytk:kg20va`l§'w8mh.71pdfoqcn[zy365x/\,-4bt9siu=j;r]e
|
||||||
|
let keyboardLayoutMap = await window.navigator.keyboard.getLayoutMap()
|
||||||
|
let k_lyts = keyboardLayoutMap.size
|
||||||
|
for (var e = "", r = window.Array.from(keyboardLayoutMap.values()), i = 0; i < r.length; i++) {
|
||||||
|
e += r[i];
|
||||||
|
}
|
||||||
|
let k_lytk = e
|
||||||
|
|
||||||
|
//}
|
||||||
|
//).catch(function (t) {
|
||||||
|
// n("k_lyts", "Err: " + Q(t.message));
|
||||||
|
//});
|
||||||
|
|
||||||
|
emd:k:ai,vi,ao
|
||||||
|
ok
|
||||||
|
wwl:false
|
||||||
|
glvd:Google Inc. (Apple)
|
||||||
|
ok
|
||||||
|
glrd:ANGLE (Apple, ANGLE Metal Renderer: Apple M2 Pro, Unspecified Version)
|
||||||
|
ok
|
||||||
|
tzp:Europe/Paris
|
||||||
|
ok: Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
|
isf:true
|
||||||
|
isf = window.outerHeight - window.innerHeight <= 1
|
||||||
|
isf2:false
|
||||||
|
isf2 = window.matchMedia && window.matchMedia("(display-mode: fullscreen)").matches
|
||||||
|
dt:false
|
||||||
|
dt = (window.outerHeight - window.innerHeight > 170)
|
||||||
|
fph:416468867
|
||||||
|
|
||||||
|
jset:1754233764
|
||||||
|
jset = Math.floor(Date.now() / 1000)
|
||||||
@@ -1 +0,0 @@
|
|||||||
jsData={"ttst":26.700000002980232,"ifov":false,"hc":8,"br_oh":803,"br_ow":407,"ua":"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36","wbd":false,"tagpu":8.178524146392085,"wdif":false,"wdifrm":false,"npmtm":false,"br_h":803,"br_w":407,"isf":true,"nddc":1,"rs_h":904,"rs_w":407,"rs_cd":24,"phe":false,"nm":false,"jsf":false,"lg":"en-US","pr":3,"ars_h":904,"ars_w":407,"tz":-120,"str_ss":true,"str_ls":true,"str_idb":true,"str_odb":false,"plgod":false,"plg":0,"plgne":"NA","plgre":"NA","plgof":"NA","plggt":"NA","pltod":false,"hcovdr":false,"hcovdr2":false,"plovdr":false,"plovdr2":false,"ftsovdr":false,"ftsovdr2":false,"lb":false,"eva":33,"lo":false,"ts_mtp":5,"ts_tec":true,"ts_tsa":true,"vnd":"Google Inc.","bid":"NA","mmt":"empty","plu":"empty","hdn":false,"awe":false,"geb":false,"dat":false,"med":"defined","aco":"probably","acots":false,"acmp":"probably","acmpts":true,"acw":"probably","acwts":false,"acma":"maybe","acmats":false,"acaa":"probably","acaats":true,"ac3":"","ac3ts":false,"acf":"probably","acfts":false,"acmp4":"maybe","acmp4ts":false,"acmp3":"probably","acmp3ts":false,"acwm":"maybe","acwmts":false,"ocpt":false,"vco":"","vcots":false,"vch":"probably","vchts":true,"vcw":"probably","vcwts":true,"vc3":"maybe","vc3ts":false,"vcmp":"","vcmpts":false,"vcq":"","vcqts":false,"vc1":"probably","vc1ts":true,"dvm":8,"sqt":false,"so":"portrait-primary","wdw":true,"cokys":"bG9hZFRpbWVzY3NpL=","ecpc":false,"lgs":true,"lgsod":false,"psn":true,"edp":false,"addt":true,"wsdc":true,"ccsr":true,"nuad":true,"bcda":true,"idn":true,"capi":false,"svde":false,"vpbq":true,"ucdv":false,"spwn":false,"emt":false,"bfr":false,"dbov":false,"cfpfe":"ZnVuY3Rpb24oKXt2YXIgdD1kb2N1bWVudFsnXHg3MVx4NzVceDY1XHg3Mlx4NzlceDUzXHg2NVx4NmNceDY1XHg2M1x4NzRceDZmXHg3MiddKCdceDYyXHg3Mlx4NmZceDc3XHg3M1x4NjVceDcyXHg2Nlx4NmNceDZmXHg3N1x4MmRceDYzXHg2Zlx4NmVceDc0XHg2","stcfp":"Z2l0YWwuaGVybWVzL3RhZ3MuanM6Mjo4MjA4NykKICAgIGF0IDxjb21wdXRlZD4gW2FzIGRkX2FjXSAoaHR0cHM6Ly9kLmRpZ2l0YWwuaGVybWVzL3RhZ3MuanM6MjoxMDE4MjgpCiAgICBhdCBodHRwczovL2QuZGlnaXRhbC5oZXJtZXMvdGFncy5qczoyOjU2OTA5","ckwa":true,"glvd":"ARM","glrd":"Mali-G610 MC6","wwl":false,"jset":1727368033}&eventCounters=[]&jsType=ch&cid=_HVXtG_qYKC_7qVGzucgKRERQZKdu0OX2QWSHlhBY2mKRFTznVsiPH1avKpVyWC2xPjcEU6HpZUeVmohtzPmY8uyqnYVsxHP_HORmhr0xm3Z73h6A2j95cLo67lwH0Pj&ddk=789361B674144528D0B7EE76B35826&Referer=https%3A%2F%2Frendezvousparis.hermes.com%2Fclient%2Fregister%2F6GJ97H&request=%2Fclient%2Fregister%2F6GJ97H&responsePage=origin&ddv=4.35.1
|
|
||||||
+1
-1
@@ -12,7 +12,7 @@ chinnese_number_prefix = ['13', '15', '18']
|
|||||||
|
|
||||||
def read_contacts(file_name) -> list:
|
def read_contacts(file_name) -> list:
|
||||||
print("read file " + file_name)
|
print("read file " + file_name)
|
||||||
contact_list_in_json = pandas.read_excel(file_name).to_json(orient='records')
|
contact_list_in_json = pandas.read_excel(file_name, dtype={'passport': str}).to_json(orient='records')
|
||||||
contact_dict_list = json.loads(contact_list_in_json)
|
contact_dict_list = json.loads(contact_list_in_json)
|
||||||
contact_list = []
|
contact_list = []
|
||||||
for contact_dict in contact_dict_list:
|
for contact_dict in contact_dict_list:
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ import time
|
|||||||
from workers.link_validator_with_provided_list import validate_all_links
|
from workers.link_validator_with_provided_list import validate_all_links
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def start_link_validation():
|
||||||
# generate test data
|
# generate test data
|
||||||
while True:
|
while True:
|
||||||
print("call validate_all_links()")
|
print("call validate_all_links()")
|
||||||
validate_all_links([])
|
validate_all_links([])
|
||||||
print("wait for 30 seconds")
|
print("wait for 30 seconds")
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
start_link_validation()
|
||||||
|
|||||||
@@ -0,0 +1,539 @@
|
|||||||
|
"""
|
||||||
|
imap_proxy_reader.py
|
||||||
|
====================
|
||||||
|
Lire des emails via IMAPClient en passant par un proxy SOCKS5/SOCKS4/HTTP.
|
||||||
|
|
||||||
|
Fonctionnement :
|
||||||
|
- ProxyIMAP4_TLS : sous-classe de imaplib.IMAP4 qui ouvre la socket
|
||||||
|
à travers un proxy SOCKS via PySocks.
|
||||||
|
- ProxyIMAPClient : sous-classe de IMAPClient qui injecte ProxyIMAP4_TLS
|
||||||
|
au lieu de la connexion directe habituelle.
|
||||||
|
|
||||||
|
Dépendances :
|
||||||
|
pip install imapclient PySocks
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import email
|
||||||
|
import imaplib
|
||||||
|
import io
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import ssl
|
||||||
|
import socket
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from email.message import Message
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
import socks
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from imapclient import IMAPClient
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Constantes
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
VALIDATION_URL_SUBJECT_FR = "Validation de votre demande de rendez-vous"
|
||||||
|
VALIDATION_URL_SUBJECT_EN = "Please confirm your appointment request"
|
||||||
|
VALIDATION_URL_REGEX = (
|
||||||
|
r"https:\/\/rendezvousparis\.hermes\.com"
|
||||||
|
r"\/client\/register\/[A-Z0-9]+\/validate\.code=[A-Z0-9]+"
|
||||||
|
)
|
||||||
|
|
||||||
|
DATE_FORMAT = "%d-%b-%Y"
|
||||||
|
|
||||||
|
# Correspondance domaine → serveur IMAP (identique à mail_constants.py)
|
||||||
|
IMAP_SERVER_MAP: List[Tuple[str, str]] = [
|
||||||
|
("163.com", "imap.163.com"),
|
||||||
|
("yahoo.com", "imap.mail.yahoo.com"),
|
||||||
|
("firemail.de", "imap.firemail.de"),
|
||||||
|
("gmail.com", "imap.gmail.com"),
|
||||||
|
("sina.com", "imap.sina.com"),
|
||||||
|
("hotmail.com", "outlook.office365.com"),
|
||||||
|
("outlook.com", "outlook.office365.com"),
|
||||||
|
("rambler.ru", "imap.rambler.ru"),
|
||||||
|
("btvm.ne.jp", "imap.btvm.ne.jp"),
|
||||||
|
("mars.dti.ne.jp", "imap.cm.dream.jp"),
|
||||||
|
("aurora.dti.ne.jp", "imap.cm.dream.jp"),
|
||||||
|
("naver.com", "imap.naver.com"),
|
||||||
|
("onet.pl", "imap.poczta.onet.pl"),
|
||||||
|
("gazeta.pl", "imap.gazeta.pl"),
|
||||||
|
("tim.it", "imap.tim.it"),
|
||||||
|
("alice.it", "in.alice.it"),
|
||||||
|
("gmx.com", "imap.gmx.com"),
|
||||||
|
("gmx.fr", "imap.gmx.com"),
|
||||||
|
("gmx.us", "imap.gmx.com"),
|
||||||
|
("gmx.ch", "imap.gmx.com"),
|
||||||
|
("gmx.pt", "imap.gmx.com"),
|
||||||
|
("gmx.sg", "imap.gmx.com"),
|
||||||
|
("gmx.net", "imap.gmx.net"),
|
||||||
|
("gmx.de", "imap.gmx.net"),
|
||||||
|
("gmx.at", "imap.gmx.at"),
|
||||||
|
("web.de", "imap.web.de"),
|
||||||
|
("inbox.lv", "mail.inbox.lv"),
|
||||||
|
("pissmail.com", "mail.pissmail.com"),
|
||||||
|
("incel.email", "mail.pissmail.com"),
|
||||||
|
("shitposting.expert","mail.pissmail.com"),
|
||||||
|
("hatesje.ws", "mail.pissmail.com"),
|
||||||
|
("child.pizza", "mail.pissmail.com"),
|
||||||
|
("genocide.fun", "mail.pissmail.com"),
|
||||||
|
("dmc.chat", "mail.pissmail.com"),
|
||||||
|
("aol.com", "imap.aol.com"), # fallback AOL
|
||||||
|
]
|
||||||
|
|
||||||
|
PROXY_TYPE_MAP = {
|
||||||
|
"SOCKS5": socks.SOCKS5,
|
||||||
|
"SOCKS4": socks.SOCKS4,
|
||||||
|
"HTTP": socks.HTTP,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Modèles de données
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProxyConfig:
|
||||||
|
"""Configuration du proxy."""
|
||||||
|
host: str
|
||||||
|
port: int
|
||||||
|
proxy_type: str = "SOCKS5" # "SOCKS5" | "SOCKS4" | "HTTP"
|
||||||
|
username: Optional[str] = None
|
||||||
|
password: Optional[str] = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def socks_type(self) -> int:
|
||||||
|
t = self.proxy_type.upper()
|
||||||
|
if t not in PROXY_TYPE_MAP:
|
||||||
|
raise ValueError(f"proxy_type invalide : {self.proxy_type!r}. "
|
||||||
|
f"Valeurs autorisées : {list(PROXY_TYPE_MAP)}")
|
||||||
|
return PROXY_TYPE_MAP[t]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
auth = f"{self.username}:***@" if self.username else ""
|
||||||
|
return f"{self.proxy_type}://{auth}{self.host}:{self.port}"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MailAccount:
|
||||||
|
"""Compte email à lire."""
|
||||||
|
login: str
|
||||||
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MailResult:
|
||||||
|
"""Résultat d'une lecture d'email."""
|
||||||
|
account: str
|
||||||
|
subject: str
|
||||||
|
from_address: str
|
||||||
|
to_address: str
|
||||||
|
body: str
|
||||||
|
validation_urls: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Connexion IMAP via proxy (bas niveau)
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class ProxyIMAP4_TLS(imaplib.IMAP4):
|
||||||
|
"""
|
||||||
|
Variante TLS de imaplib.IMAP4 qui route la connexion
|
||||||
|
à travers un proxy SOCKS5/SOCKS4/HTTP grâce à PySocks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
|
ssl_context: Optional[ssl.SSLContext],
|
||||||
|
proxy: ProxyConfig,
|
||||||
|
timeout: Optional[float] = None,
|
||||||
|
):
|
||||||
|
self._ssl_context = ssl_context
|
||||||
|
self._proxy = proxy
|
||||||
|
self._timeout = timeout
|
||||||
|
# imaplib.IMAP4.__init__ appelle self.open()
|
||||||
|
imaplib.IMAP4.__init__(self, host, port)
|
||||||
|
self.file: io.BufferedReader
|
||||||
|
|
||||||
|
def open(self, host: str = "", port: int = 993, timeout: Optional[float] = None) -> None:
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
effective_timeout = timeout if timeout is not None else self._timeout
|
||||||
|
|
||||||
|
# ── Créer la socket SOCKS ────────────────────────────
|
||||||
|
sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.set_proxy(
|
||||||
|
proxy_type=self._proxy.socks_type,
|
||||||
|
addr=self._proxy.host,
|
||||||
|
port=self._proxy.port,
|
||||||
|
username=self._proxy.username,
|
||||||
|
password=self._proxy.password,
|
||||||
|
)
|
||||||
|
if effective_timeout:
|
||||||
|
sock.settimeout(effective_timeout)
|
||||||
|
sock.connect((host, port))
|
||||||
|
|
||||||
|
# ── Envelopper avec SSL/TLS ──────────────────────────
|
||||||
|
ctx = self._ssl_context or ssl.create_default_context()
|
||||||
|
self.sock = ctx.wrap_socket(sock, server_hostname=host)
|
||||||
|
self.file = self.sock.makefile("rb")
|
||||||
|
|
||||||
|
# ── Méthodes requises par imaplib.IMAP4 ─────────────────
|
||||||
|
def read(self, size: int) -> bytes:
|
||||||
|
return self.file.read(size) # type: ignore[return-value]
|
||||||
|
|
||||||
|
def readline(self) -> bytes:
|
||||||
|
return self.file.readline() # type: ignore[return-value]
|
||||||
|
|
||||||
|
def send(self, data) -> None:
|
||||||
|
self.sock.sendall(data)
|
||||||
|
|
||||||
|
def shutdown(self) -> None:
|
||||||
|
imaplib.IMAP4.shutdown(self)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# IMAPClient avec proxy
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class ProxyIMAPClient(IMAPClient):
|
||||||
|
"""
|
||||||
|
Sous-classe d'IMAPClient qui utilise un proxy SOCKS/HTTP.
|
||||||
|
|
||||||
|
Usage :
|
||||||
|
proxy = ProxyConfig(host="127.0.0.1", port=1080, proxy_type="SOCKS5")
|
||||||
|
client = ProxyIMAPClient("imap.gmail.com", proxy=proxy, use_uid=True)
|
||||||
|
client.login("user@gmail.com", "password")
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host: str, proxy: ProxyConfig, **kwargs):
|
||||||
|
self._proxy = proxy
|
||||||
|
super().__init__(host, **kwargs)
|
||||||
|
|
||||||
|
def _create_IMAP4(self):
|
||||||
|
"""Remplace la méthode d'IMAPClient pour injecter ProxyIMAP4_TLS."""
|
||||||
|
if self.ssl:
|
||||||
|
# self._timeout peut être un float (secondes) ou un objet avec
|
||||||
|
# un attribut 'connect' (ex : urllib3 Timeout). On gère les deux.
|
||||||
|
_timeout = self._timeout
|
||||||
|
if _timeout is not None and not isinstance(_timeout, (int, float)):
|
||||||
|
_timeout = getattr(_timeout, "connect", None)
|
||||||
|
return ProxyIMAP4_TLS(
|
||||||
|
host=self.host,
|
||||||
|
port=self.port,
|
||||||
|
ssl_context=self.ssl_context,
|
||||||
|
proxy=self._proxy,
|
||||||
|
timeout=_timeout,
|
||||||
|
)
|
||||||
|
# Connexion non-SSL à travers le proxy (rare, mais supporté)
|
||||||
|
# On monkey-patch juste la connexion TCP
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Connexion IMAP non-SSL via proxy non implémentée. "
|
||||||
|
"Utilisez ssl=True (port 993)."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Fonctions utilitaires
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def get_imap_server(login: str) -> str:
|
||||||
|
"""Retourne le serveur IMAP correspondant au domaine du login."""
|
||||||
|
login_lower = login.lower()
|
||||||
|
for domain, server in IMAP_SERVER_MAP:
|
||||||
|
if domain in login_lower:
|
||||||
|
return server
|
||||||
|
return "imap.aol.com" # fallback
|
||||||
|
|
||||||
|
|
||||||
|
def extract_body(email_message: Message) -> str:
|
||||||
|
"""Extrait le corps HTML ou texte d'un email."""
|
||||||
|
body = ""
|
||||||
|
for part in email_message.walk():
|
||||||
|
content_type = part.get_content_type()
|
||||||
|
try:
|
||||||
|
if content_type == "text/html":
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
body += payload.decode("utf-8", errors="ignore")
|
||||||
|
elif content_type == "text/plain":
|
||||||
|
payload = part.get_payload()
|
||||||
|
if payload:
|
||||||
|
body += str(payload)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("Erreur extraction body : %s", exc)
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
def find_validation_urls(text: str) -> List[str]:
|
||||||
|
"""Recherche toutes les URLs de validation Hermes dans un texte."""
|
||||||
|
return re.findall(VALIDATION_URL_REGEX, text)
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Lecteur principal
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class ProxyMailReader:
|
||||||
|
"""
|
||||||
|
Lit les emails d'un compte via IMAPClient en passant par un proxy.
|
||||||
|
|
||||||
|
Paramètres
|
||||||
|
----------
|
||||||
|
account : MailAccount
|
||||||
|
Identifiants du compte email.
|
||||||
|
proxy : ProxyConfig
|
||||||
|
Configuration du proxy.
|
||||||
|
timeout : float, optional
|
||||||
|
Timeout de connexion en secondes (défaut : 30 s).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
account: MailAccount,
|
||||||
|
proxy: ProxyConfig,
|
||||||
|
timeout: float = 30.0,
|
||||||
|
):
|
||||||
|
self.account = account
|
||||||
|
self.proxy = proxy
|
||||||
|
self.timeout = timeout
|
||||||
|
|
||||||
|
# ── Connexion ────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _connect(self) -> ProxyIMAPClient:
|
||||||
|
imap_server = get_imap_server(self.account.login)
|
||||||
|
logger.info(
|
||||||
|
"[%s] Connexion via %s → %s:993",
|
||||||
|
self.account.login, self.proxy, imap_server,
|
||||||
|
)
|
||||||
|
client = ProxyIMAPClient(
|
||||||
|
host=imap_server,
|
||||||
|
proxy=self.proxy,
|
||||||
|
use_uid=True,
|
||||||
|
ssl=True,
|
||||||
|
timeout=self.timeout,
|
||||||
|
)
|
||||||
|
client.login(self.account.login, self.account.password)
|
||||||
|
logger.info("[%s] Connecté.", self.account.login)
|
||||||
|
return client
|
||||||
|
|
||||||
|
# ── Lecture des dossiers ─────────────────────────────────
|
||||||
|
|
||||||
|
def _list_folders(self, client: ProxyIMAPClient) -> List[str]:
|
||||||
|
return [info[-1] for info in client.list_folders()]
|
||||||
|
|
||||||
|
# ── Lecture des messages ─────────────────────────────────
|
||||||
|
|
||||||
|
def _read_folder(
|
||||||
|
self,
|
||||||
|
client: ProxyIMAPClient,
|
||||||
|
folder: str,
|
||||||
|
since: Optional[datetime.datetime] = None,
|
||||||
|
) -> List[MailResult]:
|
||||||
|
results: List[MailResult] = []
|
||||||
|
since = since or datetime.datetime.today()
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.select_folder(folder, readonly=True)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("[%s] Impossible d'ouvrir '%s' : %s",
|
||||||
|
self.account.login, folder, exc)
|
||||||
|
return results
|
||||||
|
|
||||||
|
try:
|
||||||
|
uids = client.search(["SINCE", since])
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("[%s] Recherche échouée dans '%s' : %s",
|
||||||
|
self.account.login, folder, exc)
|
||||||
|
return results
|
||||||
|
|
||||||
|
if not uids:
|
||||||
|
return results
|
||||||
|
|
||||||
|
logger.info("[%s] %d message(s) dans '%s'",
|
||||||
|
self.account.login, len(uids), folder)
|
||||||
|
|
||||||
|
for uid, msg_data in client.fetch(uids, "RFC822").items():
|
||||||
|
try:
|
||||||
|
raw = msg_data.get(b"RFC822") or msg_data.get("RFC822")
|
||||||
|
if raw is None:
|
||||||
|
continue
|
||||||
|
em = email.message_from_bytes(raw)
|
||||||
|
|
||||||
|
subject = em.get("Subject", "")
|
||||||
|
from_addr = em.get("From", "")
|
||||||
|
to_addr = em.get("To", self.account.login)
|
||||||
|
|
||||||
|
# Filtrer : on ne garde que les emails de validation Hermes
|
||||||
|
is_validation = (
|
||||||
|
VALIDATION_URL_SUBJECT_FR in subject
|
||||||
|
or VALIDATION_URL_SUBJECT_EN in subject
|
||||||
|
or "no-reply@hermes.com" in from_addr.lower()
|
||||||
|
)
|
||||||
|
if not is_validation:
|
||||||
|
continue
|
||||||
|
|
||||||
|
body = extract_body(em)
|
||||||
|
urls = find_validation_urls(body)
|
||||||
|
|
||||||
|
result = MailResult(
|
||||||
|
account=self.account.login,
|
||||||
|
subject=subject,
|
||||||
|
from_address=from_addr,
|
||||||
|
to_address=to_addr,
|
||||||
|
body=body,
|
||||||
|
validation_urls=urls,
|
||||||
|
)
|
||||||
|
results.append(result)
|
||||||
|
logger.info(
|
||||||
|
"[%s] Email de validation trouvé (uid=%s) — URLs : %s",
|
||||||
|
self.account.login, uid, urls or "aucune",
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning(
|
||||||
|
"[%s] Erreur traitement uid=%s : %s",
|
||||||
|
self.account.login, uid, exc,
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
# ── Point d'entrée public ────────────────────────────────
|
||||||
|
|
||||||
|
def read(
|
||||||
|
self,
|
||||||
|
since: Optional[datetime.datetime] = None,
|
||||||
|
skip_folders: Optional[List[str]] = None,
|
||||||
|
) -> List[MailResult]:
|
||||||
|
"""
|
||||||
|
Se connecte au serveur IMAP via le proxy et retourne la liste
|
||||||
|
des emails de validation trouvés depuis `since` (aujourd'hui par défaut).
|
||||||
|
|
||||||
|
Paramètres
|
||||||
|
----------
|
||||||
|
since : datetime, optional — date de début de recherche
|
||||||
|
skip_folders : list[str], optional — dossiers à ignorer
|
||||||
|
(défaut : ["Sent", "Drafts", "Trash", "Junk", "Spam"])
|
||||||
|
"""
|
||||||
|
if skip_folders is None:
|
||||||
|
skip_folders = ["Sent", "Drafts", "Trash", "Junk", "Spam"]
|
||||||
|
|
||||||
|
all_results: List[MailResult] = []
|
||||||
|
client = self._connect()
|
||||||
|
|
||||||
|
try:
|
||||||
|
folders = self._list_folders(client)
|
||||||
|
logger.info("[%s] Dossiers : %s", self.account.login, folders)
|
||||||
|
|
||||||
|
for folder in folders:
|
||||||
|
if folder in skip_folders:
|
||||||
|
logger.debug("[%s] Dossier ignoré : %s",
|
||||||
|
self.account.login, folder)
|
||||||
|
continue
|
||||||
|
all_results.extend(self._read_folder(client, folder, since))
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
client.logout()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return all_results
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Lecture parallèle de plusieurs comptes
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
|
|
||||||
|
def read_multiple_accounts(
|
||||||
|
accounts: List[MailAccount],
|
||||||
|
proxy: ProxyConfig,
|
||||||
|
since: Optional[datetime.datetime] = None,
|
||||||
|
max_workers: int = 10,
|
||||||
|
timeout: float = 30.0,
|
||||||
|
) -> List[MailResult]:
|
||||||
|
"""
|
||||||
|
Lit plusieurs comptes email en parallèle via le même proxy.
|
||||||
|
|
||||||
|
Retourne la liste consolidée de tous les MailResult trouvés.
|
||||||
|
"""
|
||||||
|
all_results: List[MailResult] = []
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
|
future_map = {
|
||||||
|
executor.submit(
|
||||||
|
ProxyMailReader(acc, proxy, timeout).read, since
|
||||||
|
): acc.login
|
||||||
|
for acc in accounts
|
||||||
|
}
|
||||||
|
for future in as_completed(future_map):
|
||||||
|
login = future_map[future]
|
||||||
|
try:
|
||||||
|
results = future.result()
|
||||||
|
logger.info("[%s] %d email(s) de validation récupéré(s).",
|
||||||
|
login, len(results))
|
||||||
|
all_results.extend(results)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("[%s] Erreur : %s", login, exc)
|
||||||
|
|
||||||
|
return all_results
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
# Point d'entrée — exemple d'utilisation
|
||||||
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s %(levelname)-8s %(message)s",
|
||||||
|
datefmt="%H:%M:%S",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── 1. Configurer le proxy ───────────────────────────────
|
||||||
|
proxy = ProxyConfig(
|
||||||
|
host=os.environ.get("GMX_PROXY_HOST", ""),
|
||||||
|
port=int(os.environ.get("GMX_PROXY_PORT", "443")),
|
||||||
|
proxy_type=os.environ.get("GMX_PROXY_TYPE", "SOCKS5"),
|
||||||
|
username=os.environ.get("GMX_PROXY_USERNAME"),
|
||||||
|
password=os.environ.get("GMX_PROXY_PASSWORD"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── 2. Définir les comptes à lire ────────────────────────
|
||||||
|
accounts = [
|
||||||
|
MailAccount(login="birgitnaya@gmx.net", password="XEeUF3Y1yaO"),
|
||||||
|
# MailAccount(login="user@gmail.com", password="apppassword"),
|
||||||
|
# MailAccount(login="user@outlook.com", password="password"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# ── 3. Lancer la lecture ─────────────────────────────────
|
||||||
|
results = read_multiple_accounts(
|
||||||
|
accounts=accounts,
|
||||||
|
proxy=proxy,
|
||||||
|
since=datetime.datetime.today(),
|
||||||
|
max_workers=5,
|
||||||
|
timeout=30.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── 4. Afficher les résultats ────────────────────────────
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f" {len(results)} email(s) de validation trouvé(s)")
|
||||||
|
print(f"{'='*60}\n")
|
||||||
|
|
||||||
|
for r in results:
|
||||||
|
print(f" Compte : {r.account}")
|
||||||
|
print(f" De : {r.from_address}")
|
||||||
|
print(f" Sujet : {r.subject}")
|
||||||
|
print(f" URLs : {r.validation_urls or 'aucune'}")
|
||||||
|
print(f" {'-'*56}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from db.mongo_manager import MONGO_STORE_MANAGER
|
||||||
|
from mail.mail_reader_all_contacts import find_links_to_validate_from_mail_list
|
||||||
|
from models.mail_pojo import MailAddress
|
||||||
|
|
||||||
|
host = "https://authhk.bhdata.com:30015/bhmailer?uid=482391396&sign=32d7748da00047b9a1054c81a5750365"
|
||||||
|
is_checking = False
|
||||||
|
|
||||||
|
HERMES_EMAIL = "no-reply@hermes.com"
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_info():
|
||||||
|
_time = str(int(time.time() * 1000))
|
||||||
|
_api_info = host + "&act=getApiInfo&t=" + _time
|
||||||
|
print(_api_info)
|
||||||
|
res = requests.get(_api_info, verify=False)
|
||||||
|
print(res.text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_mail(mail: str):
|
||||||
|
_time = str(int(time.time() * 1000))
|
||||||
|
_hermes_mail = "Votre demande de rendez-vous"
|
||||||
|
_api_info = host + "&act=getMail&email={}&title={}&t={}".format(mail, _hermes_mail, _time)
|
||||||
|
print(_api_info)
|
||||||
|
res = requests.get(_api_info, verify=False)
|
||||||
|
print(res.text)
|
||||||
|
|
||||||
|
|
||||||
|
def check_mail(mail: str):
|
||||||
|
print("check_mail called for {}".format(mail))
|
||||||
|
_time = str(int(time.time() * 1000))
|
||||||
|
_hermes_mail = HERMES_EMAIL
|
||||||
|
_subjet = "Votre rendez-vous est confirmé"
|
||||||
|
_api_info = host + "&act=checkMail&email={}&from={}&title={}&t={}".format(mail, _hermes_mail, _subjet, _time)
|
||||||
|
print(_api_info)
|
||||||
|
res = requests.get(_api_info, verify=False)
|
||||||
|
print(res.text)
|
||||||
|
|
||||||
|
|
||||||
|
def check_appointment_link_mail(mail: str):
|
||||||
|
print("check_mail called for {}".format(mail))
|
||||||
|
_time = str(int(time.time() * 1000))
|
||||||
|
_hermes_mail = HERMES_EMAIL
|
||||||
|
_subjet = "Votre demande de rendez-vous"
|
||||||
|
_api_info = host + "&act=checkMail&email={}&from={}&title={}&t={}".format(mail, _hermes_mail, _subjet, _time)
|
||||||
|
print(_api_info)
|
||||||
|
res = requests.get(_api_info, verify=False)
|
||||||
|
print(res.text)
|
||||||
|
|
||||||
|
|
||||||
|
def get_account(mail: str):
|
||||||
|
_time = str(int(time.time() * 1000))
|
||||||
|
_api_info = host + "&act=getAccount&email={}&t={}".format(mail, _time)
|
||||||
|
print(_api_info)
|
||||||
|
res = requests.get(_api_info, verify=False)
|
||||||
|
print(res.text)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_mail_with_links(_mail_list_to_filter):
|
||||||
|
_new_mail_list = []
|
||||||
|
_link_to_validate_list = MONGO_STORE_MANAGER.get_links_to_validate()
|
||||||
|
for _mail in _mail_list_to_filter:
|
||||||
|
_to_add = True
|
||||||
|
for _link in _link_to_validate_list:
|
||||||
|
if _link.email == _mail:
|
||||||
|
_to_add = False
|
||||||
|
if _to_add:
|
||||||
|
_new_mail_list.append(_mail)
|
||||||
|
return _new_mail_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_mail_list_to_check():
|
||||||
|
successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
||||||
|
_mail_list = []
|
||||||
|
for _item in successful_items:
|
||||||
|
if _item.url_validated is None or _item.url_validated != True:
|
||||||
|
_mail_list.append(_item.email)
|
||||||
|
return _mail_list
|
||||||
|
|
||||||
|
|
||||||
|
def check_confirmed_mails():
|
||||||
|
successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
||||||
|
# _mail_list = []
|
||||||
|
for _item in successful_items:
|
||||||
|
if "outlook" in _item.email or "hotmail" in _item.email:
|
||||||
|
check_mail(_item.email)
|
||||||
|
time.sleep(random.randint(1, 5))
|
||||||
|
|
||||||
|
|
||||||
|
def check_all_need_to_check_emails():
|
||||||
|
global is_checking
|
||||||
|
if not is_checking:
|
||||||
|
is_checking = True
|
||||||
|
logger = logging.getLogger()
|
||||||
|
_mail_list_before_filter = get_mail_list_to_check()
|
||||||
|
_mails = filter_mail_with_links(_mail_list_before_filter)
|
||||||
|
for _mail in _mails:
|
||||||
|
if "outlook.com" in _mail or "hotmail.com" in _mail:
|
||||||
|
check_mail(_mail)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# _mail_list = [MailAddress("saigecong1990@pissmail.com", "cvExXKOP8oY1D@")]
|
||||||
|
# find_links_to_validate_from_mail_list(_mail_list, logger)
|
||||||
|
is_checking = False
|
||||||
|
|
||||||
|
|
||||||
|
def try_to_check_all_mails():
|
||||||
|
logger = logging.getLogger()
|
||||||
|
_mail_list_before_filter = get_mail_list_to_check()
|
||||||
|
_mails = filter_mail_with_links(_mail_list_before_filter)
|
||||||
|
for _mail in _mails:
|
||||||
|
if "outlook.com" in _mail or "hotmail.com" in _mail:
|
||||||
|
check_appointment_link_mail(_mail)
|
||||||
|
time.sleep(2)
|
||||||
|
# _mail_list = [MailAddress("saigecong1990@pissmail.com", "cvExXKOP8oY1D@")]
|
||||||
|
# find_links_to_validate_from_mail_list(_mail_list, logger)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
check_all_need_to_check_emails()
|
||||||
|
# try_to_check_all_mails()
|
||||||
|
# check_confirmed_mails()
|
||||||
|
# check_appointment_link_mail("hcunlvi533@outlook.com")
|
||||||
+33
-51
@@ -2,6 +2,7 @@ import imaplib
|
|||||||
|
|
||||||
from imapclient import IMAPClient
|
from imapclient import IMAPClient
|
||||||
|
|
||||||
|
# 邮件域名常量
|
||||||
DOMAIN_YAHOO = "yahoo.com"
|
DOMAIN_YAHOO = "yahoo.com"
|
||||||
DOMAIN_SINA = "sina.com"
|
DOMAIN_SINA = "sina.com"
|
||||||
DOMAIN_HOTMAIL = "hotmail.com"
|
DOMAIN_HOTMAIL = "hotmail.com"
|
||||||
@@ -10,9 +11,9 @@ DOMAIN_163 = "163.com"
|
|||||||
DOMAIN_RAMBLER_RU = "rambler.ru"
|
DOMAIN_RAMBLER_RU = "rambler.ru"
|
||||||
DOMAIN_ALICE_IT = "alice.it"
|
DOMAIN_ALICE_IT = "alice.it"
|
||||||
DOMAIN_MARS_DTI_NE_JP = "mars.dti.ne.jp"
|
DOMAIN_MARS_DTI_NE_JP = "mars.dti.ne.jp"
|
||||||
DOMAN_BTVM_NE_JP = "btvm.ne.jp"
|
DOMAIN_BTVM_NE_JP = "btvm.ne.jp"
|
||||||
DOMAN_AURORA_DTI_NE_JP = "aurora.dti.ne.jp"
|
DOMAIN_AURORA_DTI_NE_JP = "aurora.dti.ne.jp"
|
||||||
DOMAN_GMAIL = "gmail.com"
|
DOMAIN_GMAIL = "gmail.com"
|
||||||
DOMAIN_GMX = "gmx.com"
|
DOMAIN_GMX = "gmx.com"
|
||||||
DOMAIN_GMX_NET = "gmx.net"
|
DOMAIN_GMX_NET = "gmx.net"
|
||||||
DOMAIN_GMX_AT = "gmx.at"
|
DOMAIN_GMX_AT = "gmx.at"
|
||||||
@@ -27,6 +28,7 @@ DOMAIN_NAVER = "naver.com"
|
|||||||
DOMAIN_INBOX_LV = "inbox.lv"
|
DOMAIN_INBOX_LV = "inbox.lv"
|
||||||
DOMAIN_GMX_DE = "gmx.de"
|
DOMAIN_GMX_DE = "gmx.de"
|
||||||
|
|
||||||
|
# 垃圾邮件域名
|
||||||
DOMAIN_PISS_MAIL = "pissmail.com"
|
DOMAIN_PISS_MAIL = "pissmail.com"
|
||||||
DOMAIN_INCEL_EMAIL = "incel.email"
|
DOMAIN_INCEL_EMAIL = "incel.email"
|
||||||
DOMAIN_SHITPOSTING_EXPERT = "shitposting.expert"
|
DOMAIN_SHITPOSTING_EXPERT = "shitposting.expert"
|
||||||
@@ -38,6 +40,7 @@ DOMAIN_WEB_DE = "web.de"
|
|||||||
DOMAIN_OUTLOOK_COM = "outlook.com"
|
DOMAIN_OUTLOOK_COM = "outlook.com"
|
||||||
DOMAIN_FIREMAIL_DE = "firemail.de"
|
DOMAIN_FIREMAIL_DE = "firemail.de"
|
||||||
|
|
||||||
|
# IMAP服务器地址常量
|
||||||
AOL_IMAP_SERVER = "imap.aol.com"
|
AOL_IMAP_SERVER = "imap.aol.com"
|
||||||
IMAP_SERVER_163 = "imap.163.com"
|
IMAP_SERVER_163 = "imap.163.com"
|
||||||
IMAP_SERVER_SINA = "imap.sina.com"
|
IMAP_SERVER_SINA = "imap.sina.com"
|
||||||
@@ -49,18 +52,17 @@ ALICE_IMAP_SERVER = "in.alice.it"
|
|||||||
TIME_IT_SERVER = "imap.tim.it"
|
TIME_IT_SERVER = "imap.tim.it"
|
||||||
MARS_DTI_NE_JP_SERVER = "imap.cm.dream.jp"
|
MARS_DTI_NE_JP_SERVER = "imap.cm.dream.jp"
|
||||||
NAVER_SERVER = "imap.naver.com"
|
NAVER_SERVER = "imap.naver.com"
|
||||||
BTVM_NE_JP = "imap.btvm.ne.jp"
|
BTVM_NE_JP_SERVER = "imap.btvm.ne.jp"
|
||||||
SEREVER_GMAIL = "imap.gmail.com"
|
GMAIL_IMAP_SERVER = "imap.gmail.com"
|
||||||
SERVER_IMAGE_ONET = "imap.poczta.onet.pl"
|
ONET_IMAP_SERVER = "imap.poczta.onet.pl"
|
||||||
SERVER_GMX = "imap.gmx.com"
|
GMX_IMAP_SERVER = "imap.gmx.com"
|
||||||
SERVER_GMX_NET = "imap.gmx.net"
|
GMX_NET_IMAP_SERVER = "imap.gmx.net"
|
||||||
SERVER_GMX_AT = "imap.gmx.at"
|
GMX_AT_IMAP_SERVER = "imap.gmx.at"
|
||||||
SERVER_FIREMAIL_DE = "imap.firemail.de"
|
FIREMAIL_DE_IMAP_SERVER = "imap.firemail.de"
|
||||||
SERVER_PISS_MAIL = "mail.pissmail.com"
|
PISS_MAIL_IMAP_SERVER = "mail.pissmail.com"
|
||||||
INBOX_LV = "mail.inbox.lv"
|
INBOX_LV_IMAP_SERVER = "mail.inbox.lv"
|
||||||
SERVER_WEB_DE = "imap.web.de"
|
WEB_DE_IMAP_SERVER = "imap.web.de"
|
||||||
IMAP_SERVER_DOMAIN_GAZETA_PL = "imap.gazeta.pl"
|
GAZETA_PL_IMAP_SERVER = "imap.gazeta.pl"
|
||||||
|
|
||||||
|
|
||||||
def show_folders(imap) -> list:
|
def show_folders(imap) -> list:
|
||||||
folders = []
|
folders = []
|
||||||
@@ -82,69 +84,49 @@ def show_folders(imap) -> list:
|
|||||||
|
|
||||||
|
|
||||||
def create_imap(login: str):
|
def create_imap(login: str):
|
||||||
# create an IMAP4 class with SSL
|
# 创建一个IMAP4类实例
|
||||||
if DOMAIN_163 in login:
|
if DOMAIN_163 in login:
|
||||||
imap = IMAPClient(IMAP_SERVER_163, use_uid=True)
|
imap = IMAPClient(IMAP_SERVER_163, use_uid=True)
|
||||||
elif DOMAIN_YAHOO in login:
|
elif DOMAIN_YAHOO in login:
|
||||||
# imap = imaplib.IMAP4_SSL(YAHOO_IMAP_SERVER)
|
|
||||||
imap = IMAPClient(YAHOO_IMAP_SERVER, use_uid=True)
|
imap = IMAPClient(YAHOO_IMAP_SERVER, use_uid=True)
|
||||||
elif DOMAIN_FIREMAIL_DE in login:
|
elif DOMAIN_FIREMAIL_DE in login:
|
||||||
# imap = imaplib.IMAP4_SSL(SERVER_FIREMAIL_DE)
|
imap = IMAPClient(FIREMAIL_DE_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(SERVER_FIREMAIL_DE, use_uid=True)
|
|
||||||
elif DOMAIN_GMX in login or DOMAIN_GMX_FR in login or DOMAIN_GMX_US in login or DOMAIN_GMX_CH in login or DOMAIN_GMX_PT in login or DOMAIN_GMX_SG in login:
|
elif DOMAIN_GMX in login or DOMAIN_GMX_FR in login or DOMAIN_GMX_US in login or DOMAIN_GMX_CH in login or DOMAIN_GMX_PT in login or DOMAIN_GMX_SG in login:
|
||||||
# imap = imaplib.IMAP4_SSL(SERVER_GMX)
|
imap = IMAPClient(GMX_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(SERVER_GMX, use_uid=True)
|
|
||||||
elif DOMAIN_SINA in login:
|
elif DOMAIN_SINA in login:
|
||||||
# imap = imaplib.IMAP4_SSL(IMAP_SERVER_SINA)
|
|
||||||
imap = IMAPClient(IMAP_SERVER_SINA, use_uid=True)
|
imap = IMAPClient(IMAP_SERVER_SINA, use_uid=True)
|
||||||
elif DOMAIN_HOTMAIL in login or DOMAIN_OUTLOOK_COM in login:
|
elif DOMAIN_HOTMAIL in login or DOMAIN_OUTLOOK_COM in login:
|
||||||
# imap = imaplib.IMAP4_SSL(HOTMAIL_IMAP_SERVER)
|
|
||||||
imap = IMAPClient(HOTMAIL_IMAP_SERVER, use_uid=True)
|
imap = IMAPClient(HOTMAIL_IMAP_SERVER, use_uid=True)
|
||||||
elif DOMAIN_RAMBLER_RU in login:
|
elif DOMAIN_RAMBLER_RU in login:
|
||||||
# imap = imaplib.IMAP4_SSL(RAMBLER_IMAP_SERVER)
|
|
||||||
imap = IMAPClient(RAMBLER_IMAP_SERVER, use_uid=True)
|
imap = IMAPClient(RAMBLER_IMAP_SERVER, use_uid=True)
|
||||||
elif DOMAN_BTVM_NE_JP in login:
|
elif DOMAIN_BTVM_NE_JP in login:
|
||||||
# imap = imaplib.IMAP4_SSL(BTVM_NE_JP)
|
imap = IMAPClient(BTVM_NE_JP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(BTVM_NE_JP, use_uid=True)
|
elif DOMAIN_GMAIL in login:
|
||||||
elif DOMAN_GMAIL in login:
|
imap = IMAPClient(GMAIL_IMAP_SERVER, use_uid=True)
|
||||||
# imap = imaplib.IMAP4_SSL(SEREVER_GMAIL, port=993)
|
|
||||||
imap = IMAPClient(SEREVER_GMAIL, use_uid=True)
|
|
||||||
elif DOMAIN_ONET in login:
|
elif DOMAIN_ONET in login:
|
||||||
imap = IMAPClient(SERVER_IMAGE_ONET, use_uid=True)
|
imap = IMAPClient(ONET_IMAP_SERVER, use_uid=True)
|
||||||
elif DOMAIN_TIM_IT in login:
|
elif DOMAIN_TIM_IT in login:
|
||||||
# imap = imaplib.IMAP4(TIME_IT_SERVER)
|
|
||||||
imap = IMAPClient(TIME_IT_SERVER, use_uid=True)
|
imap = IMAPClient(TIME_IT_SERVER, use_uid=True)
|
||||||
elif DOMAIN_ALICE_IT in login:
|
elif DOMAIN_ALICE_IT in login:
|
||||||
# imap = imaplib.IMAP4(ALICE_IMAP_SERVER, port=143)
|
|
||||||
imap = IMAPClient(ALICE_IMAP_SERVER, use_uid=True)
|
imap = IMAPClient(ALICE_IMAP_SERVER, use_uid=True)
|
||||||
elif DOMAIN_MARS_DTI_NE_JP in login:
|
elif DOMAIN_MARS_DTI_NE_JP in login:
|
||||||
# imap = imaplib.IMAP4(MARS_DTI_NE_JP_SERVER, port=143)
|
|
||||||
imap = IMAPClient(MARS_DTI_NE_JP_SERVER, use_uid=True)
|
imap = IMAPClient(MARS_DTI_NE_JP_SERVER, use_uid=True)
|
||||||
elif DOMAN_AURORA_DTI_NE_JP in login:
|
elif DOMAIN_AURORA_DTI_NE_JP in login:
|
||||||
# imap = imaplib.IMAP4(MARS_DTI_NE_JP_SERVER, port=143)
|
|
||||||
imap = IMAPClient(MARS_DTI_NE_JP_SERVER, use_uid=True)
|
imap = IMAPClient(MARS_DTI_NE_JP_SERVER, use_uid=True)
|
||||||
elif DOMAIN_NAVER in login:
|
elif DOMAIN_NAVER in login:
|
||||||
# imap = imaplib.IMAP4_SSL(NAVER_SERVER, port=993)
|
|
||||||
imap = IMAPClient(NAVER_SERVER, use_uid=True)
|
imap = IMAPClient(NAVER_SERVER, use_uid=True)
|
||||||
elif DOMAIN_GMX_DE in login or DOMAIN_GMX_NET in login:
|
elif DOMAIN_GMX_DE in login or DOMAIN_GMX_NET in login:
|
||||||
# imap = imaplib.IMAP4_SSL(SERVER_GMX_NET, port=993)
|
imap = IMAPClient(GMX_NET_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(SERVER_GMX_NET, use_uid=True)
|
|
||||||
elif DOMAIN_GMX_AT in login:
|
elif DOMAIN_GMX_AT in login:
|
||||||
# imap = imaplib.IMAP4_SSL(SERVER_GMX_AT, port=993)
|
imap = IMAPClient(GMX_AT_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(SERVER_GMX_AT, use_uid=True)
|
|
||||||
elif DOMAIN_GAZETA_PL in login:
|
elif DOMAIN_GAZETA_PL in login:
|
||||||
# imap = imaplib.IMAP4_SSL(IMAP_SERVER_DOMAIN_GAZETA_PL, port=993)
|
imap = IMAPClient(GAZETA_PL_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(IMAP_SERVER_DOMAIN_GAZETA_PL, use_uid=True)
|
|
||||||
elif DOMAIN_INBOX_LV in login:
|
elif DOMAIN_INBOX_LV in login:
|
||||||
# imap = imaplib.IMAP4_SSL(INBOX_LV, port=993)
|
imap = IMAPClient(INBOX_LV_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(INBOX_LV, use_uid=True)
|
|
||||||
elif DOMAIN_WEB_DE in login:
|
elif DOMAIN_WEB_DE in login:
|
||||||
# imap = imaplib.IMAP4_SSL(SERVER_WEB_DE, port=993)
|
imap = IMAPClient(WEB_DE_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(SERVER_WEB_DE, use_uid=True)
|
|
||||||
elif DOMAIN_PISS_MAIL in login or DOMAIN_CHILD_PIZZA in login or DOMAIN_DMC_CHAT in login or DOMAIN_GENOCIDE_FUN in login or DOMAIN_HATESJE_WS in login or DOMAIN_INCEL_EMAIL in login or DOMAIN_SHITPOSTING_EXPERT in login:
|
elif DOMAIN_PISS_MAIL in login or DOMAIN_CHILD_PIZZA in login or DOMAIN_DMC_CHAT in login or DOMAIN_GENOCIDE_FUN in login or DOMAIN_HATESJE_WS in login or DOMAIN_INCEL_EMAIL in login or DOMAIN_SHITPOSTING_EXPERT in login:
|
||||||
# imap = imaplib.IMAP4_SSL(SERVER_PISS_MAIL, port=993)
|
imap = IMAPClient(PISS_MAIL_IMAP_SERVER, use_uid=True)
|
||||||
imap = IMAPClient(SERVER_PISS_MAIL, use_uid=True)
|
|
||||||
else:
|
else:
|
||||||
# imap = imaplib.IMAP4_SSL(AOL_IMAP_SERVER)
|
|
||||||
imap = IMAPClient(AOL_IMAP_SERVER, use_uid=True)
|
imap = IMAPClient(AOL_IMAP_SERVER, use_uid=True)
|
||||||
return imap
|
return imap
|
||||||
Executable → Regular
+407
-157
@@ -1,88 +1,171 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import email
|
import email
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from builtins import list
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from email.header import decode_header
|
from email.header import decode_header
|
||||||
from email.message import Message
|
from email.message import Message
|
||||||
from typing import Union
|
from typing import Union, List, Optional
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
from imapclient import IMAPClient
|
from imapclient import IMAPClient
|
||||||
from db.mongo_manager import MONGO_STORE_MANAGER
|
from db.mongo_manager import MONGO_STORE_MANAGER
|
||||||
from excel_reader import read_contacts
|
from excel_reader import read_contacts
|
||||||
from mail.mail_constants import DOMAIN_HOTMAIL, create_imap
|
from mail.mail_constants import DOMAIN_HOTMAIL, create_imap
|
||||||
|
from mail.imap_proxy_reader import ProxyIMAPClient, ProxyConfig, get_imap_server
|
||||||
from models.ReserveResultPojo import ReserveResultPojo
|
from models.ReserveResultPojo import ReserveResultPojo
|
||||||
from models.mail_pojo import MailPojo, MailAddress
|
from models.mail_pojo import MailPojo, MailAddress
|
||||||
|
|
||||||
VALIDATION_URL_SUBJECT_fr = 'Validation de votre demande de rendez-vous'
|
# Charger les variables d'environnement depuis .env
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# 定义常量
|
||||||
|
VALIDATION_URL_SUBJECT_FR = 'Validation de votre demande de rendez-vous'
|
||||||
VALIDATION_URL_SUBJECT_EN = 'Please confirm your appointment request'
|
VALIDATION_URL_SUBJECT_EN = 'Please confirm your appointment request'
|
||||||
VALIDATION_URL_REGEX = """https:\/\/rendezvousparis.hermes.com\/client\/register\/[A-Z0-9]+\/validate.code=[A-Z0-9]+"""
|
VALIDATION_URL_REGEX = r"https:\/\/rendezvousparis.hermes.com\/client\/register\/[A-Z0-9]+\/validate.code=[A-Z0-9]+"
|
||||||
PART_VALIDATION_URL_REGEX = """client\/register\/[A-Z0-9]+\/validate.code=[A-Z0-9]+"""
|
PART_VALIDATION_URL_REGEX = r"client\/register\/[A-Z0-9]+\/validate.code=[A-Z0-9]+"
|
||||||
HERMES_EMAIL = "no-reply@hermes.com"
|
HERMES_EMAIL = "no-reply@hermes.com"
|
||||||
EMAIL_ADDRESS_REGEX = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
|
EMAIL_ADDRESS_REGEX = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
|
||||||
|
|
||||||
date_format = "%d-%b-%Y" # DD-Mon-YYYY e.g., 3-Mar-2014
|
# 日期格式
|
||||||
|
DATE_FORMAT = "%d-%b-%Y"
|
||||||
|
|
||||||
|
# Timeouts GMX (en secondes)
|
||||||
|
IMAP_SOCKET_TIMEOUT = 300 # timeout socket pour chaque opération IMAP
|
||||||
|
FUTURE_TIMEOUT = 600 # durée max allouée à la lecture d'une boîte mail
|
||||||
|
|
||||||
|
# GMX域名列表(用于判断是否需要使用代理)
|
||||||
|
GMX_DOMAINS = (
|
||||||
|
"gmx.com", "gmx.net", "gmx.de", "gmx.at",
|
||||||
|
"gmx.fr", "gmx.us", "gmx.sg", "gmx.ch", "gmx.pt",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 需要通过代理读取的域名列表
|
||||||
|
# PROXY_DOMAINS = GMX_DOMAINS + ("inbox.lv",)
|
||||||
|
PROXY_DOMAINS = GMX_DOMAINS
|
||||||
|
|
||||||
|
|
||||||
|
def is_gmx_account(login: str) -> bool:
|
||||||
|
"""判断邮箱是否属于GMX域名"""
|
||||||
|
return any(d in login.lower() for d in GMX_DOMAINS)
|
||||||
|
|
||||||
|
|
||||||
|
def is_proxy_account(login: str) -> bool:
|
||||||
|
"""判断邮箱是否需要通过代理读取(GMX 或 inbox.lv)"""
|
||||||
|
return any(d in login.lower() for d in PROXY_DOMAINS)
|
||||||
|
|
||||||
|
# 邮箱列表(简化为常量)
|
||||||
REDIRECTION_MAILS = "appointment2022@aol.com, chenpeijun@aol.com,hongjiang176@aol.com,ciyuexie@aol.com,rutger.62@aol.com,ciccidaniel@aol.com,armasgoodman@aol.com,wknd.gemerine@aol.com,rafmail1981@aol.com,tonovichivanenaki@aol.com,hetland.ari@aol.com,mateusiversen@aol.com,lacerdaraffaello@aol.com,anasida76@aol.com,liamolinari@aol.com,sen70zib@aol.com,mezeiderrick@aol.com,stanisl49avchic@aol.com,damcvrobaneuron@aol.com,suyzanna_fleona@aol.com,dxealing.dissa@aol.com,hogg.karen@aol.com,obocharovamarina@aol.com,buchholzjohann@aol.com,orn.cecchini@aol.com,percivaltorgersen@aol.com,candalgudrun@aol.com,filimonis.76@aol.com,bengann_100@aol.com,axelhanne@aol.com,tiffanylarochelle@aol.com,nicoleta.r@aol.com,eichenbaum.1963@aol.com,kotensasharev@aol.com,samognat32@aol.com,edem_headshot@aol.com,kozmakuzmich1960@aol.com,damonsvensson@aol.com,anders.riva@aol.com,caiminwei123@gmail.com,yulingguo086@gmail.com,yingxiaolu086@gmail.com,lijiazhen0035@gmail.com,fangp370@gmail.com,huangyayu10086@gmail.com,fuziyuan110@gmail.com,xinyingdu886@gmail.com,yasiaforever.1971@aol.com,lukaszfidalgo@aol.com,zaichi29@aol.com,prostotakitak.1974@aol.com,mo90nroe@aol.com,blonde.87@aol.com,dimidrol.1969@aol.com"
|
REDIRECTION_MAILS = "appointment2022@aol.com, chenpeijun@aol.com,hongjiang176@aol.com,ciyuexie@aol.com,rutger.62@aol.com,ciccidaniel@aol.com,armasgoodman@aol.com,wknd.gemerine@aol.com,rafmail1981@aol.com,tonovichivanenaki@aol.com,hetland.ari@aol.com,mateusiversen@aol.com,lacerdaraffaello@aol.com,anasida76@aol.com,liamolinari@aol.com,sen70zib@aol.com,mezeiderrick@aol.com,stanisl49avchic@aol.com,damcvrobaneuron@aol.com,suyzanna_fleona@aol.com,dxealing.dissa@aol.com,hogg.karen@aol.com,obocharovamarina@aol.com,buchholzjohann@aol.com,orn.cecchini@aol.com,percivaltorgersen@aol.com,candalgudrun@aol.com,filimonis.76@aol.com,bengann_100@aol.com,axelhanne@aol.com,tiffanylarochelle@aol.com,nicoleta.r@aol.com,eichenbaum.1963@aol.com,kotensasharev@aol.com,samognat32@aol.com,edem_headshot@aol.com,kozmakuzmich1960@aol.com,damonsvensson@aol.com,anders.riva@aol.com,caiminwei123@gmail.com,yulingguo086@gmail.com,yingxiaolu086@gmail.com,lijiazhen0035@gmail.com,fangp370@gmail.com,huangyayu10086@gmail.com,fuziyuan110@gmail.com,xinyingdu886@gmail.com,yasiaforever.1971@aol.com,lukaszfidalgo@aol.com,zaichi29@aol.com,prostotakitak.1974@aol.com,mo90nroe@aol.com,blonde.87@aol.com,dimidrol.1969@aol.com"
|
||||||
|
|
||||||
|
|
||||||
def check_email_address(email):
|
# 邮件处理相关函数
|
||||||
# pass the regular expression
|
def is_valid_email(email: str) -> bool:
|
||||||
# and the string into the fullmatch() method
|
"""验证邮箱地址是否有效"""
|
||||||
if (re.fullmatch(EMAIL_ADDRESS_REGEX, email)):
|
return re.fullmatch(EMAIL_ADDRESS_REGEX, email) is not None
|
||||||
print("Valid Email")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("Invalid Email:" + email)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def find_from_mail(param):
|
def extract_email_from_from_address(content: str) -> str:
|
||||||
|
"""从邮件地址中提取邮箱"""
|
||||||
|
match = re.search(r'[\w.+-]+@[\w-]+\.[\w.-]+', content)
|
||||||
|
return match.group(0) if match else ""
|
||||||
|
|
||||||
|
|
||||||
|
def find_from_mail(param) -> str:
|
||||||
|
"""解析邮件地址"""
|
||||||
from_address, encoded_algo = param[0]
|
from_address, encoded_algo = param[0]
|
||||||
|
|
||||||
|
# 处理字节编码
|
||||||
if isinstance(from_address, bytes):
|
if isinstance(from_address, bytes):
|
||||||
from_address = from_address.decode(encoded_algo)
|
from_address = from_address.decode(encoded_algo)
|
||||||
if not check_email_address(from_address) and len(param) == 2:
|
|
||||||
|
# 如果邮箱地址无效,尝试另一种编码
|
||||||
|
if not is_valid_email(from_address) and len(param) == 2:
|
||||||
from_address, new_encode = param[1]
|
from_address, new_encode = param[1]
|
||||||
if new_encode is None:
|
if new_encode is None:
|
||||||
new_encode = encoded_algo
|
new_encode = encoded_algo
|
||||||
if isinstance(from_address, bytes):
|
if isinstance(from_address, bytes):
|
||||||
from_address = from_address.decode(new_encode)
|
from_address = from_address.decode(new_encode)
|
||||||
return from_address.strip(" ").strip(">").strip("<")
|
|
||||||
|
# 清理邮箱地址
|
||||||
return from_address.strip(" ").strip(">").strip("<")
|
return from_address.strip(" ").strip(">").strip("<")
|
||||||
|
|
||||||
|
|
||||||
def extract_email_from_from_address(content: str):
|
class MailReader:
|
||||||
_match = re.search(r'[\w.+-]+@[\w-]+\.[\w.-]+', content)
|
"""邮件读取器类"""
|
||||||
return _match.group(0)
|
|
||||||
|
|
||||||
|
def __init__(self, login: str, password: str, proxy: Optional[ProxyConfig] = None,
|
||||||
class MailReader():
|
failed_gmx_list: Optional[List[str]] = None):
|
||||||
def __init__(self, login, password):
|
|
||||||
self.login = login
|
self.login = login
|
||||||
self.password = password
|
self.password = password
|
||||||
|
self.proxy = proxy
|
||||||
|
self.failed_gmx_list = failed_gmx_list if failed_gmx_list is not None else []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def show_folders(imap) -> list:
|
def show_folders(imap) -> List[str]:
|
||||||
|
"""获取邮箱文件夹列表"""
|
||||||
folders = []
|
folders = []
|
||||||
isImapClient = isinstance(imap, IMAPClient)
|
is_imap_client = isinstance(imap, IMAPClient)
|
||||||
if not isImapClient:
|
|
||||||
|
if not is_imap_client:
|
||||||
|
# 处理非IMAPClient对象
|
||||||
for i in imap.list()[1]:
|
for i in imap.list()[1]:
|
||||||
l = i.decode().split(' "/" ')
|
l = i.decode().split(' "/" ')
|
||||||
folders.append(l[1])
|
folders.append(l[1])
|
||||||
return folders
|
|
||||||
else:
|
else:
|
||||||
list = imap.list_folders()
|
# 处理IMAPClient对象
|
||||||
for i in list:
|
folder_list = imap.list_folders()
|
||||||
|
for i in folder_list:
|
||||||
name = i[-1]
|
name = i[-1]
|
||||||
folders.append(name)
|
folders.append(name)
|
||||||
return folders
|
|
||||||
|
|
||||||
def read_emails(self, mails_messages: list) -> list:
|
return folders
|
||||||
imap = create_imap(self.login)
|
|
||||||
isImapClient = isinstance(imap, IMAPClient)
|
def read_emails(self, mails_messages: List[MailPojo]) -> List[MailPojo]:
|
||||||
print("isImapClient is " + str(isImapClient))
|
"""读取邮件"""
|
||||||
if isImapClient:
|
# ── GMX / inbox.lv 账户 → 使用代理连接(失败自动重试最多3次)──
|
||||||
# authenticate
|
if is_proxy_account(self.login) and self.proxy is not None:
|
||||||
|
return self._read_emails_with_proxy_retry(mails_messages)
|
||||||
|
else:
|
||||||
|
return self._read_emails_internal(create_imap(self.login), mails_messages)
|
||||||
|
|
||||||
|
def _read_emails_with_proxy_retry(
|
||||||
|
self,
|
||||||
|
mails_messages: List[MailPojo],
|
||||||
|
max_retries: int = 8,
|
||||||
|
) -> List[MailPojo]:
|
||||||
|
"""通过 ProxyIMAPClient 读取邮件(GMX / inbox.lv),失败时最多重试 max_retries 次。"""
|
||||||
|
imap_server = get_imap_server(self.login)
|
||||||
|
last_error: Optional[Exception] = None
|
||||||
|
|
||||||
|
for attempt in range(1, max_retries + 1):
|
||||||
|
try:
|
||||||
|
print("[Proxy] {} → {} via {} (tentative {}/{})".format(
|
||||||
|
self.login, imap_server, self.proxy, attempt, max_retries))
|
||||||
|
imap = ProxyIMAPClient(
|
||||||
|
host=imap_server,
|
||||||
|
proxy=self.proxy,
|
||||||
|
use_uid=True,
|
||||||
|
ssl=True,
|
||||||
|
timeout=IMAP_SOCKET_TIMEOUT,
|
||||||
|
)
|
||||||
|
return self._read_emails_internal(imap, mails_messages)
|
||||||
|
except Exception as exc:
|
||||||
|
last_error = exc
|
||||||
|
print("[Proxy] Échec tentative {}/{} pour {} : {}".format(
|
||||||
|
attempt, max_retries, self.login, exc))
|
||||||
|
|
||||||
|
print("[Proxy] Toutes les tentatives ont échoué pour {} : {}".format(
|
||||||
|
self.login, last_error))
|
||||||
|
self.failed_gmx_list.append(self.login)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _read_emails_internal(self, imap, mails_messages: List[MailPojo]) -> List[MailPojo]:
|
||||||
|
"""Logique commune de lecture des emails (IMAPClient ou imaplib)."""
|
||||||
|
is_imap_client = isinstance(imap, IMAPClient)
|
||||||
|
|
||||||
|
# 登录邮箱
|
||||||
|
if is_imap_client:
|
||||||
dat = imap.login(self.login, str(self.password))
|
dat = imap.login(self.login, str(self.password))
|
||||||
print("type is {} for {}".format(dat, self.login))
|
print("type is {} for {}".format(dat, self.login))
|
||||||
else:
|
else:
|
||||||
@@ -91,211 +174,352 @@ class MailReader():
|
|||||||
|
|
||||||
mail_list = []
|
mail_list = []
|
||||||
print("read mails from {}".format(self.login))
|
print("read mails from {}".format(self.login))
|
||||||
if not isImapClient:
|
|
||||||
folder_list = self.show_folders(imap)
|
# 获取文件夹列表
|
||||||
for folder in folder_list:
|
folder_list = self.show_folders(imap)
|
||||||
print("folder is {}".format(folder))
|
|
||||||
mail_list.extend(self._get_messages_from_folder(imap, subject=VALIDATION_URL_SUBJECT_fr,
|
# 处理每个文件夹
|
||||||
folder=folder))
|
for folder in folder_list:
|
||||||
mail_list.extend(self._get_messages_from_folder(imap, subject=VALIDATION_URL_SUBJECT_EN,
|
print("folder is {}".format(folder))
|
||||||
folder=folder))
|
|
||||||
else:
|
# 跳过Sent和Drafts文件夹
|
||||||
folder_list = self.show_folders(imap)
|
if folder in ["Sent", "Drafts"]:
|
||||||
for folder in folder_list:
|
continue
|
||||||
print("folder is " + folder)
|
|
||||||
mail_list.extend(self._get_messages_from_folder_for_imapclient(imap, folder=folder))
|
if is_imap_client:
|
||||||
if not isImapClient:
|
# 使用IMAPClient处理
|
||||||
|
mail_list.extend(self._get_messages_from_folder_for_imapclient(imap, folder))
|
||||||
|
else:
|
||||||
|
# 使用传统IMAP处理
|
||||||
|
mail_list.extend(self._get_messages_from_folder(imap, subject=VALIDATION_URL_SUBJECT_FR, folder=folder))
|
||||||
|
mail_list.extend(self._get_messages_from_folder(imap, subject=VALIDATION_URL_SUBJECT_EN, folder=folder))
|
||||||
|
|
||||||
|
# 关闭连接
|
||||||
|
if not is_imap_client:
|
||||||
imap.close()
|
imap.close()
|
||||||
imap.logout()
|
imap.logout()
|
||||||
|
|
||||||
|
# 添加邮件到结果列表
|
||||||
mails_messages.extend(mail_list)
|
mails_messages.extend(mail_list)
|
||||||
return mail_list
|
return mail_list
|
||||||
|
|
||||||
def _get_messages_from_folder(self, imap, subject, folder="INBOX") -> list:
|
def _get_messages_from_folder(self, imap, subject: str, folder: str = "INBOX") -> List[MailPojo]:
|
||||||
|
"""从指定文件夹获取邮件(传统IMAP方式)"""
|
||||||
imap.select(folder)
|
imap.select(folder)
|
||||||
mail_messages = []
|
mail_messages = []
|
||||||
typ, data = imap.search(None, '(SUBJECT "{}" SINCE "{}")'.format(subject,
|
|
||||||
datetime.datetime.today().strftime(
|
# 搜索邮件
|
||||||
date_format)))
|
search_query = '(SUBJECT "{}" SINCE "{}")'.format(subject, datetime.datetime.today().strftime(DATE_FORMAT))
|
||||||
|
typ, data = imap.search(None, search_query)
|
||||||
|
|
||||||
for i in data[0].split():
|
for i in data[0].split():
|
||||||
# fetch the email message by ID
|
try:
|
||||||
res, msg = imap.fetch(i.decode("utf-8"), "(RFC822)")
|
# 获取邮件内容
|
||||||
body = ''
|
res, msg = imap.fetch(i.decode("utf-8"), "(RFC822)")
|
||||||
for response in msg:
|
|
||||||
if isinstance(response, tuple):
|
# 解析邮件
|
||||||
# parse a bytes email into a message object
|
for response in msg:
|
||||||
msg = email.message_from_bytes(response[1])
|
if isinstance(response, tuple):
|
||||||
# decode the email subject
|
email_message = email.message_from_bytes(response[1])
|
||||||
subject, subject_encoded = decode_header(msg["Subject"])[0]
|
|
||||||
received_date = msg["Date"]
|
# 解码主题
|
||||||
if isinstance(subject, bytes):
|
subject, subject_encoded = decode_header(email_message["Subject"])[0]
|
||||||
# if it's a bytes, decode to str
|
if isinstance(subject, bytes):
|
||||||
subject = subject.decode(subject_encoded)
|
subject = subject.decode(subject_encoded)
|
||||||
# decode email sender
|
|
||||||
from_address = find_from_mail(decode_header(msg.get("From")))
|
# 解码发件人地址
|
||||||
to_email = find_from_mail(decode_header(msg.get("To")))
|
from_address = find_from_mail(decode_header(email_message.get("From")))
|
||||||
print("Email:", self.login)
|
|
||||||
print("From:", from_address)
|
# 解码收件人地址
|
||||||
print("To:", to_email)
|
to_email = find_from_mail(decode_header(email_message.get("To")))
|
||||||
print("Subject:", subject)
|
|
||||||
# if the email message is multipart
|
print("Email:", self.login)
|
||||||
if msg.is_multipart():
|
print("From:", from_address)
|
||||||
# iterate over email parts
|
print("To:", to_email)
|
||||||
for part in msg.walk():
|
print("Subject:", subject)
|
||||||
try:
|
|
||||||
# get the email body
|
# 获取邮件正文
|
||||||
payloads = part.get_payload()
|
body = self._extract_body(email_message)
|
||||||
if isinstance(payloads, list):
|
|
||||||
for payload in payloads:
|
# 检查是否是预约验证邮件
|
||||||
if isinstance(payload, Message):
|
if VALIDATION_URL_SUBJECT_FR in subject or VALIDATION_URL_SUBJECT_EN in subject:
|
||||||
body = body + payload.get_payload(decode=True).decode("iso-8859-1")
|
mail = MailPojo(
|
||||||
# print(body)
|
subject=subject,
|
||||||
except Exception as Error:
|
body=body,
|
||||||
print(Error)
|
from_address=from_address
|
||||||
else:
|
)
|
||||||
body = msg.get_payload(decode=True).decode()
|
|
||||||
print(body)
|
# 设置收件人地址
|
||||||
if VALIDATION_URL_SUBJECT_fr in subject or VALIDATION_URL_SUBJECT_EN in subject:
|
if to_email is None:
|
||||||
mail = MailPojo(subject=subject, body=body, from_address=from_address)
|
mail.to_address = self.login
|
||||||
if to_email is None:
|
else:
|
||||||
mail.to_address = self.login
|
mail.to_address = to_email
|
||||||
else:
|
|
||||||
mail.to_address = to_email
|
mail.mail_address = self.login
|
||||||
mail.mail_address = self.login
|
mail_messages.append(mail)
|
||||||
mail_messages.append(mail)
|
except Exception as error:
|
||||||
|
print("Error processing email: {}".format(error))
|
||||||
|
|
||||||
return mail_messages
|
return mail_messages
|
||||||
|
|
||||||
def _get_messages_from_folder_for_imapclient(self, imap, folder="INBOX") -> list:
|
def _extract_body(self, email_message: Message) -> str:
|
||||||
|
"""提取邮件正文"""
|
||||||
|
body = ""
|
||||||
|
|
||||||
|
# 遍历邮件部分
|
||||||
|
for part in email_message.walk():
|
||||||
|
try:
|
||||||
|
content_type = part.get_content_type()
|
||||||
|
|
||||||
|
if content_type == "text/html":
|
||||||
|
# 处理HTML内容
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
body += payload.decode("utf-8", errors="ignore")
|
||||||
|
elif content_type == "text/plain":
|
||||||
|
# 处理纯文本内容
|
||||||
|
payload = part.get_payload()
|
||||||
|
if payload:
|
||||||
|
body += payload
|
||||||
|
except Exception as error:
|
||||||
|
print("Error extracting body part: {}".format(error))
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
def _get_messages_from_folder_for_imapclient(self, imap, folder: str = "INBOX") -> List[MailPojo]:
|
||||||
|
"""从指定文件夹获取邮件(IMAPClient方式)"""
|
||||||
mail_messages = []
|
mail_messages = []
|
||||||
|
|
||||||
|
# 搜索邮件
|
||||||
search_terms = 'SINCE "{}"'.format(
|
search_terms = 'SINCE "{}"'.format(
|
||||||
datetime.datetime.today().strftime(
|
datetime.datetime.today().strftime(DATE_FORMAT))
|
||||||
date_format))
|
print("{}: search terms is {}".format(self.login, search_terms))
|
||||||
print("search terms is " + search_terms)
|
|
||||||
imap.select_folder(folder)
|
imap.select_folder(folder)
|
||||||
messages = imap.search(['SINCE', datetime.datetime.today()])
|
messages = imap.search(['SINCE', datetime.datetime.today()])
|
||||||
print("%d messages from our best friend" % len(messages))
|
print("{}: {} messages from our best friend".format(self.login, len(messages)))
|
||||||
|
|
||||||
|
if len(messages) == 0:
|
||||||
|
return mail_messages
|
||||||
|
|
||||||
|
# 处理每封邮件
|
||||||
for uid, message_data in imap.fetch(messages, 'RFC822').items():
|
for uid, message_data in imap.fetch(messages, 'RFC822').items():
|
||||||
try:
|
try:
|
||||||
email_message = email.message_from_bytes(message_data[b'RFC822'])
|
email_message = email.message_from_bytes(message_data[b'RFC822'])
|
||||||
|
|
||||||
|
# 获取发件人和主题
|
||||||
from_address = email_message.get('FROM')
|
from_address = email_message.get('FROM')
|
||||||
subject = email_message.get('subject')
|
subject = email_message.get('subject')
|
||||||
body = ""
|
|
||||||
hermes_mail_adress = "no-reply@hermes.com"
|
# 检查是否是Hermes邮件
|
||||||
if hermes_mail_adress in from_address or "outlook.com" in from_address or "hotmail" in from_address:
|
hermes_mail_address = "no-reply@hermes.com"
|
||||||
for part in email_message.walk():
|
if (hermes_mail_address in from_address or
|
||||||
print(part.get_content_type())
|
"outlook.com" in from_address or
|
||||||
if part.get_content_type() == "text/html":
|
"hotmail" in from_address):
|
||||||
body = body + part.get_payload(decode=True).decode("utf-8")
|
|
||||||
elif part.get_content_type() == "text/plain":
|
# 提取邮件正文
|
||||||
body = body + part.get_payload()
|
body = self._extract_body_for_imapclient(email_message)
|
||||||
if VALIDATION_URL_SUBJECT_fr in subject or VALIDATION_URL_SUBJECT_EN in subject or "Votre=20demande=20de=20rendez-vous" in subject or "Votre demande de rendez-vous" in body:
|
|
||||||
mail = MailPojo(subject=subject, body=body, from_address=from_address)
|
# 检查是否是预约验证邮件
|
||||||
|
if (VALIDATION_URL_SUBJECT_FR in subject or
|
||||||
|
VALIDATION_URL_SUBJECT_EN in subject or
|
||||||
|
"Votre=20demande=20de=20rendez-vous" in subject or
|
||||||
|
"Votre demande de rendez-vous" in body):
|
||||||
|
|
||||||
|
mail = MailPojo(
|
||||||
|
subject=subject,
|
||||||
|
body=body,
|
||||||
|
from_address=from_address
|
||||||
|
)
|
||||||
mail.isImapClient = True
|
mail.isImapClient = True
|
||||||
|
|
||||||
print("email is {}".format(self.login))
|
print("email is {}".format(self.login))
|
||||||
print("body is {}".format(body))
|
print("body is {}".format(body))
|
||||||
print("subject is {}".format(subject))
|
print("subject is {}".format(subject))
|
||||||
|
|
||||||
|
# 设置收件人地址
|
||||||
if len(mail.to_address) == 0:
|
if len(mail.to_address) == 0:
|
||||||
if "outlook.com" in from_address or "hotmail.com" in from_address:
|
if "outlook.com" in from_address or "hotmail.com" in from_address:
|
||||||
# it is a transferred email
|
# 转发邮件
|
||||||
mail.to_address = extract_email_from_from_address(from_address)
|
mail.to_address = extract_email_from_from_address(from_address)
|
||||||
else:
|
else:
|
||||||
mail.to_address = self.login
|
mail.to_address = self.login
|
||||||
|
|
||||||
mail_messages.append(mail)
|
mail_messages.append(mail)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
print(error)
|
print("Error trying to read email_Message for {}: {}".format(self.login, error))
|
||||||
print("error trying to read email_Message for {}".format(self.login))
|
|
||||||
return mail_messages
|
return mail_messages
|
||||||
|
|
||||||
|
def _extract_body_for_imapclient(self, email_message: Message) -> str:
|
||||||
|
"""提取IMAPClient邮件正文"""
|
||||||
|
body = ""
|
||||||
|
|
||||||
#
|
for part in email_message.walk():
|
||||||
# Find the ReserveResultPojo object from persisted items of DB
|
content_type = part.get_content_type()
|
||||||
#
|
|
||||||
|
if content_type == "text/html":
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
body += payload.decode("utf-8", errors="ignore")
|
||||||
|
elif content_type == "text/plain":
|
||||||
|
payload = part.get_payload()
|
||||||
|
if payload:
|
||||||
|
body += payload
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
# 邮件处理相关函数
|
||||||
def find_item_by_url(url: str, successful_items) -> Union[None, ReserveResultPojo]:
|
def find_item_by_url(url: str, successful_items) -> Union[None, ReserveResultPojo]:
|
||||||
|
"""根据URL查找预约结果对象"""
|
||||||
print("url is :" + url)
|
print("url is :" + url)
|
||||||
parts = url.split('/')
|
parts = url.split('/')
|
||||||
_id = parts[5]
|
_id = parts[5]
|
||||||
|
|
||||||
if len(_id) == 6:
|
if len(_id) == 6:
|
||||||
for item in successful_items:
|
for item in successful_items:
|
||||||
if item.id == _id:
|
if item.id == _id:
|
||||||
return item
|
return item
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def need_to_valid_url(url: str, item: Union[ReserveResultPojo, None]) -> bool:
|
def need_to_valid_url(url: str, item: Union[ReserveResultPojo, None]) -> bool:
|
||||||
|
"""判断是否需要验证URL"""
|
||||||
print("url is :" + url)
|
print("url is :" + url)
|
||||||
parts = url.split('/')
|
parts = url.split('/')
|
||||||
id = parts[5]
|
_id = parts[5]
|
||||||
if len(id) == 6:
|
|
||||||
|
if len(_id) == 6:
|
||||||
if item:
|
if item:
|
||||||
if item.url_validated is not None:
|
if item.url_validated is not None:
|
||||||
return not item.url_validated
|
return not item.url_validated
|
||||||
else:
|
else:
|
||||||
# if url_validated is None
|
# 如果url_validated为None,需要验证
|
||||||
return True
|
return True
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
print("id not valid:{}".format(id))
|
print("id not valid:{}".format(_id))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def need_to_check_email(mail: str, successful_items) -> bool:
|
def need_to_check_email(mail: str, successful_items) -> bool:
|
||||||
|
"""判断是否需要检查邮件"""
|
||||||
print("successful_items size is " + str(len(successful_items)))
|
print("successful_items size is " + str(len(successful_items)))
|
||||||
filtered_items = list(filter(lambda item: item.email == mail, successful_items))
|
|
||||||
# has validated value
|
# 过滤已验证的项目
|
||||||
if len(filtered_items) > 0:
|
filtered_items = [item for item in successful_items if item.email == mail]
|
||||||
validated_items = list(filter(
|
|
||||||
lambda filtered_item: filtered_item.url_validated is not None and filtered_item.url_validated is True,
|
# 检查是否有已验证的项目
|
||||||
filtered_items))
|
validated_items = [item for item in filtered_items
|
||||||
if len(validated_items) > 0:
|
if item.url_validated is not None and item.url_validated is True]
|
||||||
return False
|
|
||||||
else:
|
return len(validated_items) == 0
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def find_links_to_validate_from_mail_list(mail_list: list, logger):
|
def find_links_to_validate_from_mail_list(
|
||||||
|
mail_list: List[MailAddress],
|
||||||
|
logger,
|
||||||
|
proxy: Optional[ProxyConfig] = None,
|
||||||
|
) -> List[str]:
|
||||||
|
"""从邮件列表中查找需要验证的链接,返回读取失败的GMX账户列表"""
|
||||||
if not mail_list:
|
if not mail_list:
|
||||||
return
|
return []
|
||||||
# check time before start checking emails
|
|
||||||
|
# 检查时间前开始检查邮件
|
||||||
contact_to_book_list = MONGO_STORE_MANAGER.get_all_contact_to_book_list()
|
contact_to_book_list = MONGO_STORE_MANAGER.get_all_contact_to_book_list()
|
||||||
successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
||||||
|
|
||||||
mails_messages = []
|
mails_messages = []
|
||||||
with ThreadPoolExecutor(max_workers=len(mail_list)) as executor:
|
failed_gmx: List[str] = []
|
||||||
|
|
||||||
|
# 使用线程池处理邮件
|
||||||
|
with ThreadPoolExecutor(max_workers=100) as executor:
|
||||||
|
futures = []
|
||||||
|
|
||||||
for mail in mail_list:
|
for mail in mail_list:
|
||||||
# check whether we need to read mail
|
# 检查是否需要读取邮件
|
||||||
if need_to_check_email(mail.mail, successful_items):
|
if need_to_check_email(mail.mail, successful_items):
|
||||||
mail_reader = MailReader(mail.mail, mail.password)
|
mail_reader = MailReader(mail.mail, mail.password, proxy=proxy,
|
||||||
executor.submit(mail_reader.read_emails, mails_messages)
|
failed_gmx_list=failed_gmx)
|
||||||
|
future = executor.submit(mail_reader.read_emails, mails_messages)
|
||||||
|
futures.append(future)
|
||||||
|
|
||||||
|
# 等待所有任务完成
|
||||||
|
for future in futures:
|
||||||
|
try:
|
||||||
|
future.result(timeout=FUTURE_TIMEOUT)
|
||||||
|
except TimeoutError:
|
||||||
|
print("⏱️ Timeout ({} s) dépassé pour une boîte mail — lecture ignorée.".format(FUTURE_TIMEOUT))
|
||||||
|
except Exception as e:
|
||||||
|
print("Error processing mail: {},login: {}, password: {}".format(e,mail.mail, mail.password))
|
||||||
|
|
||||||
|
# ── Résumé des comptes proxy en échec ──────────────────────
|
||||||
|
if failed_gmx:
|
||||||
|
print("\n[Proxy] ⚠️ {} compte(s) non lus (GMX / inbox.lv) :".format(len(failed_gmx)))
|
||||||
|
for addr in failed_gmx:
|
||||||
|
print(" ✗ {}".format(addr))
|
||||||
|
else:
|
||||||
|
print("\n[Proxy] ✅ Tous les comptes GMX / inbox.lv ont été lus avec succès.")
|
||||||
|
|
||||||
|
# 刷新成功的项目
|
||||||
_refreshed_successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
_refreshed_successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
||||||
|
|
||||||
|
# 处理邮件中的链接
|
||||||
for mail in mails_messages:
|
for mail in mails_messages:
|
||||||
match = re.search(VALIDATION_URL_REGEX, mail.body)
|
match = re.search(VALIDATION_URL_REGEX, mail.body)
|
||||||
if match:
|
if match:
|
||||||
url = match.group(0)
|
url = match.group(0)
|
||||||
_item = find_item_by_url(url, _refreshed_successful_items)
|
_item = find_item_by_url(url, _refreshed_successful_items)
|
||||||
|
|
||||||
if need_to_valid_url(url, _item):
|
if need_to_valid_url(url, _item):
|
||||||
logger.info("need to validate url: " + url)
|
logger.info("need to validate url: " + url)
|
||||||
MONGO_STORE_MANAGER.save_links_to_validate(url, mail.to_address, model=_item.model,
|
_model = ""
|
||||||
_all_contact_list=contact_to_book_list)
|
_used_ip = ""
|
||||||
|
if _item:
|
||||||
|
_model = _item.model
|
||||||
|
_used_ip = _item.current_ip
|
||||||
|
|
||||||
|
MONGO_STORE_MANAGER.save_links_to_validate(
|
||||||
|
url,
|
||||||
|
mail.to_address,
|
||||||
|
model=_model,
|
||||||
|
_all_contact_list=contact_to_book_list, _used_ip= _used_ip)
|
||||||
else:
|
else:
|
||||||
logger.info("do not need to click url --> {}".format(mail.mail_address))
|
logger.info("do not need to click url --> {}".format(mail.mail_address))
|
||||||
|
|
||||||
|
return failed_gmx
|
||||||
|
|
||||||
|
|
||||||
|
# 主函数
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# mail_address1 = MailAddress(mail="tinagonzales685585@aol.com", password="yhihvdkrbxnksema")
|
# 读取联系人列表
|
||||||
# mail_list = [mail_address1]
|
|
||||||
contact_to_book_list = read_contacts(
|
contact_to_book_list = read_contacts(
|
||||||
file_name="/Users/lpan/Desktop/contact_list_2025-06-27_gmx.xlsx")
|
# file_name="~/Desktop/contact_list_inbox_lv_100.xlsx")
|
||||||
# file_name="/Users/rdv/Desktop/contact_list_all_studio.xlsx")
|
# file_name="~/Desktop/contact_yahoo_5.xlsx")
|
||||||
# file_name="/Users/rdv/Desktop/contact_list_all_studo_gmx_us.xlsx")
|
# file_name="~/Desktop/contact_list_2026-04-07.xlsx")
|
||||||
# file_name="/Users/rdv/Desktop/contact_list_2025-05-24.xlsx")
|
# file_name="~/Desktop/contact_list_2026-04-11.xlsx")
|
||||||
|
file_name="~/Desktop/contact_list_2026-04-15.xlsx")
|
||||||
|
# file_name="~/Desktop/contact_list_inbox_100_14_04.xlsx")
|
||||||
|
# file_name="~/Desktop/yahooo_list.xlsx")
|
||||||
|
|
||||||
|
# 获取目标邮箱列表
|
||||||
all_mail_list = MONGO_STORE_MANAGER.get_destination_emails()
|
all_mail_list = MONGO_STORE_MANAGER.get_destination_emails()
|
||||||
|
|
||||||
|
# 筛选需要检查的邮件列表
|
||||||
mail_list_to_check = []
|
mail_list_to_check = []
|
||||||
for contact in contact_to_book_list:
|
for contact in contact_to_book_list:
|
||||||
for mail in all_mail_list:
|
for mail in all_mail_list:
|
||||||
if contact.mail == mail.mail:
|
if contact.mail == mail.mail:
|
||||||
mail_list_to_check.append(mail)
|
mail_list_to_check.append(mail)
|
||||||
|
|
||||||
|
# 设置日志记录器
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
# 获取已验证的链接列表
|
||||||
_all_links = MONGO_STORE_MANAGER.get_links_to_validate()
|
_all_links = MONGO_STORE_MANAGER.get_links_to_validate()
|
||||||
|
|
||||||
|
# 过滤掉已处理的邮件
|
||||||
filter_mail = []
|
filter_mail = []
|
||||||
for mail_pojo in mail_list_to_check:
|
for mail_pojo in mail_list_to_check:
|
||||||
_to_add = True
|
_to_add = True
|
||||||
@@ -304,5 +528,31 @@ if __name__ == '__main__':
|
|||||||
_to_add = False
|
_to_add = False
|
||||||
if _to_add:
|
if _to_add:
|
||||||
filter_mail.append(mail_pojo)
|
filter_mail.append(mail_pojo)
|
||||||
filter_mail.append(MailAddress("saigecong1990@pissmail.com", "cvExXKOP8oY1D@"))
|
# filter_mail = [MailAddress("munozshawn1992@aol.com", "leivqvcwyacrgbzp")]
|
||||||
find_links_to_validate_from_mail_list(filter_mail, logger)
|
|
||||||
|
# ── Mode de lecture : GMX_ONLY=true → uniquement les comptes GMX ──
|
||||||
|
gmx_only = os.environ.get("GMX_ONLY", "false").strip().lower() == "true"
|
||||||
|
if gmx_only:
|
||||||
|
filter_mail = [m for m in filter_mail if is_gmx_account(m.mail)]
|
||||||
|
print("[Mode] Lecture GMX uniquement ({} comptes)".format(len(filter_mail)))
|
||||||
|
else:
|
||||||
|
print("[Mode] Lecture de tous les comptes ({} comptes)".format(len(filter_mail)))
|
||||||
|
|
||||||
|
# 配置代理(GMX账号必须通过代理读取)
|
||||||
|
gmx_proxy = ProxyConfig(
|
||||||
|
host=os.environ.get("GMX_PROXY_HOST", ""),
|
||||||
|
port=int(os.environ.get("GMX_PROXY_PORT", "443")),
|
||||||
|
proxy_type=os.environ.get("GMX_PROXY_TYPE", "SOCKS5"),
|
||||||
|
username=os.environ.get("GMX_PROXY_USERNAME"),
|
||||||
|
password=os.environ.get("GMX_PROXY_PASSWORD"),
|
||||||
|
)
|
||||||
|
# 处理邮件
|
||||||
|
failed = find_links_to_validate_from_mail_list(filter_mail, logger, proxy=gmx_proxy)
|
||||||
|
|
||||||
|
# ── Afficher les comptes GMX non lus ─────────────────────
|
||||||
|
if failed:
|
||||||
|
print("\n===== Comptes GMX non lus ({}) =====".format(len(failed)))
|
||||||
|
for addr in failed:
|
||||||
|
print(" ✗ {}".format(addr))
|
||||||
|
else:
|
||||||
|
print("\n===== Tous les comptes GMX ont été lus avec succès =====")
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
|
|
||||||
from queue_message.CookiesPublisher import MORNING_DATA_CACHE_2, MORNING_DATA_CACHE, MORNING_DATA_CACHE_BAK
|
from queue_message.CookiesPublisher import MORNING_DATA_CACHE_2, MORNING_DATA_CACHE, MORNING_DATA_CACHE_BAK, \
|
||||||
|
REGISTER_QUEUE
|
||||||
from workers.MessagerTransporter import migrate_message_to_queue
|
from workers.MessagerTransporter import migrate_message_to_queue
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# p1 = Process(target=migrate_message_to_queue, args=(MORNING_DATA_CACHE_2, MORNING_DATA_CACHE_BAK))
|
p1 = Process(target=migrate_message_to_queue, args=(MORNING_DATA_CACHE, MORNING_DATA_CACHE_BAK))
|
||||||
# p1.start()
|
p1.start()
|
||||||
p2 = Process(target=migrate_message_to_queue, args=(MORNING_DATA_CACHE, MORNING_DATA_CACHE_BAK))
|
p2 = Process(target=migrate_message_to_queue, args=(MORNING_DATA_CACHE_2, MORNING_DATA_CACHE_BAK))
|
||||||
p2.start()
|
p2.start()
|
||||||
# p2.join()
|
p2.join()
|
||||||
# migrate_message_to_queue(from_queue=MORNING_DATA_CACHE_2)
|
# migrate_message_to_queue(from_queue=MORNING_DATA_CACHE_2)
|
||||||
# migrate_message_to_queue(from_queue=MORNING_DATA_CACHE)
|
# migrate_message_to_queue(from_queue=MORNING_DATA_CACHE)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ class PublishType(Enum):
|
|||||||
@dataclass_json
|
@dataclass_json
|
||||||
@dataclass
|
@dataclass
|
||||||
class ReserveResultPojo:
|
class ReserveResultPojo:
|
||||||
type: PublishType = PublishType.ERROR
|
|
||||||
phone: str = ""
|
phone: str = ""
|
||||||
message: str = ""
|
message: str = ""
|
||||||
url: str = ""
|
url: str = ""
|
||||||
@@ -38,14 +37,10 @@ class ReserveResultPojo:
|
|||||||
proxy: str = None
|
proxy: str = None
|
||||||
ua: str = None
|
ua: str = None
|
||||||
current_ip: str = ""
|
current_ip: str = ""
|
||||||
|
timestampInS: list = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_firestore_dict(source):
|
def from_firestore_dict(source):
|
||||||
publish_type = PublishType.ERROR
|
|
||||||
if 'type' in source:
|
|
||||||
publish_type = source['type']
|
|
||||||
if publish_type:
|
|
||||||
publish_type = PublishType[publish_type]
|
|
||||||
if 'phone' in source:
|
if 'phone' in source:
|
||||||
phone = source['phone']
|
phone = source['phone']
|
||||||
else:
|
else:
|
||||||
@@ -54,8 +49,6 @@ class ReserveResultPojo:
|
|||||||
url = source['url']
|
url = source['url']
|
||||||
else:
|
else:
|
||||||
url = ""
|
url = ""
|
||||||
if 'id' in source:
|
|
||||||
id = source['id']
|
|
||||||
if '_id' in source:
|
if '_id' in source:
|
||||||
id = source['_id']
|
id = source['_id']
|
||||||
else:
|
else:
|
||||||
@@ -74,7 +67,7 @@ class ReserveResultPojo:
|
|||||||
else:
|
else:
|
||||||
firstName = ""
|
firstName = ""
|
||||||
|
|
||||||
result = ReserveResultPojo(type=publish_type, phone=phone,
|
result = ReserveResultPojo(phone=phone,
|
||||||
url=url, email=email,
|
url=url, email=email,
|
||||||
firstName=firstName, lastName=lastName)
|
firstName=firstName, lastName=lastName)
|
||||||
if 'accepted' in source:
|
if 'accepted' in source:
|
||||||
@@ -113,12 +106,14 @@ class ReserveResultPojo:
|
|||||||
if 'validated_at' in source:
|
if 'validated_at' in source:
|
||||||
validated_at = source['validated_at']
|
validated_at = source['validated_at']
|
||||||
result.validated_at = validated_at
|
result.validated_at = validated_at
|
||||||
|
if 'current_ip' in source:
|
||||||
|
current_ip = source['current_ip']
|
||||||
|
result.current_ip = current_ip
|
||||||
result.id = id
|
result.id = id
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def to_firestore_dict(self):
|
def to_firestore_dict(self):
|
||||||
dest = {
|
dest = {
|
||||||
u'type': self.type.value,
|
|
||||||
u'id': self.id,
|
u'id': self.id,
|
||||||
u'phone': self.phone,
|
u'phone': self.phone,
|
||||||
u'firstName': self.firstName,
|
u'firstName': self.firstName,
|
||||||
@@ -135,6 +130,7 @@ class ReserveResultPojo:
|
|||||||
u'url_validated': self.url_validated,
|
u'url_validated': self.url_validated,
|
||||||
u'proxy': self.proxy,
|
u'proxy': self.proxy,
|
||||||
u'current_ip': self.current_ip,
|
u'current_ip': self.current_ip,
|
||||||
|
u'timestamp_in_s': "-".join(str(x) for x in self.timestampInS),
|
||||||
u'ua': self.ua,
|
u'ua': self.ua,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from captcha.jspl_encoder_wrapper import encrpte_to_jspl
|
||||||
from models.jsdata_pojo import JsDataPojo
|
from models.jsdata_pojo import JsDataPojo
|
||||||
|
|
||||||
|
|
||||||
@@ -21,9 +23,29 @@ from models.jsdata_pojo import JsDataPojo
|
|||||||
class JsDataLeTypePojo(JsDataPojo):
|
class JsDataLeTypePojo(JsDataPojo):
|
||||||
|
|
||||||
def __init__(self, glrd, glvd, hc, br_oh, ua, br_ow, br_h, br_w, rs_h, rs_w, rs_cd, ars_h, ars_w, plg, eva, plu,
|
def __init__(self, glrd, glvd, hc, br_oh, ua, br_ow, br_h, br_w, rs_h, rs_w, rs_cd, ars_h, ars_w, plg, eva, plu,
|
||||||
vnd, pr, ts_mtp, dvm, m_s_c, m_m_c, m_c_c, m_cm_r, m_ms_r, emd: str):
|
vnd, pr, ts_mtp, dvm, m_s_c, m_m_c, m_c_c, m_cm_r, m_ms_r, emd: str, ccsB,
|
||||||
|
br_iw=513, br_ih=847, pf="Linux armv81", mob=True,
|
||||||
|
lgs=["fr-FR", "en-US", "zh-CN", "zh", "fr", "en"],
|
||||||
|
orf="debug",
|
||||||
|
bchk="3223aeb6721e0d0917e7928181193ac88dcd62fad5cadfbe7a2b2b473ecf58ee70f098dbdb1a1832e8dc6528387b0745971dbcd82384261e9a4e3f",
|
||||||
|
nt_tcp=0, nt_dns=0, nt_rd=0, nt_irt=-3.400000002235174, nt_rt=0, nt_tls=3.400000002235174,
|
||||||
|
nt_ttf=3.099999997764826, nt_swt=3.400000002235174, nt_csd=0, nt_nhp="", nt_rdc=0, nt_it="navigation",
|
||||||
|
nt_prs=0, nt_esc=-3.400000002235174, nt_ttrd=-2, nt_le=0, nt_dcle=0.10000000149011612,
|
||||||
|
nt_di=74.80000000074506, nt_dc=0, ccsT="""Error
|
||||||
|
at S (https://d.digital.hermes/tags.js:173:22)
|
||||||
|
at Un.C (https://d.digital.hermes/tags.js:1252:41
|
||||||
|
at nrWrapper (https://www.hermes.com/fr/fr/category/femme/Sacs%20et%20pochettes%20pour%20Femme%20_%20Herme%CC%80s%20France.html:7:13468)""",
|
||||||
|
ccsH="1050544242",
|
||||||
|
ccsV=",993b46baf0942a343b7e6b02fa3f8eef64727f077d3b0055af56e6994dcaf046", muev=False, prso=True,
|
||||||
|
nhi=",64,true,Nexus 5,Android,6.0,138.0.7204.184,false", k_lyts=48,
|
||||||
|
k_lytk="kg20va`l§'w8mh.71pdfoqcn[zy365x/\,-4bt9siu=j;r]e", tzp="Europe/Paris", isf2=False):
|
||||||
super().__init__(glrd, glvd, hc, br_oh, ua, br_ow, br_h, br_w, rs_h, rs_w, rs_cd, ars_h, ars_w, plg, eva, plu,
|
super().__init__(glrd, glvd, hc, br_oh, ua, br_ow, br_h, br_w, rs_h, rs_w, rs_cd, ars_h, ars_w, plg, eva, plu,
|
||||||
vnd, pr, ts_mtp, dvm, emd=emd)
|
vnd, pr, ts_mtp, dvm, emd=emd, ccsB=ccsB, br_iw=br_iw, br_ih=br_ih, pf=pf, mob=mob, lgs=lgs,
|
||||||
|
orf=orf, bchk=bchk, nt_tcp=nt_tcp, nt_dns=nt_dns, nt_rd=nt_rd, nt_irt=nt_irt, nt_rt=nt_rt,
|
||||||
|
nt_tls=nt_tls, nt_ttf=nt_ttf, nt_swt=nt_swt, nt_csd=nt_csd, nt_nhp=nt_nhp, nt_rdc=nt_rdc,
|
||||||
|
nt_it=nt_it, nt_prs=nt_prs, nt_esc=nt_esc, nt_ttrd=nt_ttrd, nt_le=nt_le, nt_dcle=nt_dcle,
|
||||||
|
nt_di=nt_di, nt_dc=nt_dc, ccsT=ccsT, ccsH=ccsH, ccsV=ccsV, muev=muev, prso=prso, nhi=nhi,
|
||||||
|
k_lyts=k_lyts, k_lytk=k_lytk, tzp=tzp, isf2=isf2)
|
||||||
self.jset = int(time.time()) # override from 4_34_2
|
self.jset = int(time.time()) # override from 4_34_2
|
||||||
self.emd = emd
|
self.emd = emd
|
||||||
self.dcok = ".hermes.com"
|
self.dcok = ".hermes.com"
|
||||||
@@ -50,14 +72,32 @@ class JsDataLeTypePojo(JsDataPojo):
|
|||||||
self.m_c_c = m_c_c
|
self.m_c_c = m_c_c
|
||||||
self.m_cm_r = m_cm_r
|
self.m_cm_r = m_cm_r
|
||||||
self.m_ms_r = m_ms_r
|
self.m_ms_r = m_ms_r
|
||||||
self.cfpfe = "ZnVuY3Rpb24oKXt2YXIgbj10LGk9ZG9jdW1lbnRbJ1x4NzFceDc1XHg2NVx4NzJceDc5XHg1M1x4NjVceDZjXHg2NVx4NjNceDc0XHg2Zlx4NzInXShuKDQ2MSkpO2lmKGkpeyFmdW5jdGlvbiB0KCl7dHJ5e3ZhciBuPWlbJ1x4NzNceDY4XHg2MVx4NjRceDZmXHg3" # 4.40.0
|
self.cfpfe = "KHIsbixvLGEpPT57aWYoZVtyXSllW3JdLnB1c2gobik7ZWxzZXt2YXIgcyxjO2lmKHZvaWQgMCE9PW8pZm9yKHZhciB1PWRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCJzY3JpcHQiKSxkPTA7ZDx1Lmxlbmd0aDtkKyspe3ZhciBsPXVbZF07aWYobC5nZXRB" # 5.1.8
|
||||||
self.stcfp = "Ly9kLmRpZ2l0YWwuaGVybWVzL3RhZ3MuanM6Mjo4ODYzNSkKICAgIGF0IHQuZXhwb3J0cy5kZF9hZCAoaHR0cHM6Ly9kLmRpZ2l0YWwuaGVybWVzL3RhZ3MuanM6MjoxMDc3OTYpCiAgICBhdCBodHRwczovL2QuZGlnaXRhbC5oZXJtZXMvdGFncy5qczoyOjYzNzkw" # 4.40.0
|
self.stcfp = "JTIwSGVybWUlQ0MlODBzJTIwRnJhbmNlLmh0bTozNDoyNjY4OSkKYXRvIChmaWxlOi8vL1VzZXJzL3BhbmxlaS9Eb3dubG9hZHMvQnJhY2VsZXQlMjBDbGljJTIwSEglMjAtJTIwTWFycm9uJTIwXyUyMEhlcm1lJUNDJTgwcyUyMEZyYW5jZS5odG06MzQ6MzAzNzgp" # 5.1.8
|
||||||
|
|
||||||
|
|
||||||
test_data_json = """{"glvd":"ARM","glrd":"Mali-G57 MC2","hc":5,"br_oh":620,"br_ow":360,"br_h":620,"br_w":360,"rs_h":804,"rs_w":360,"rs_cd":24,"ars_h":804,"ars_w":360,"plg":2,"eva":33,"vnd":"Google Inc.","plu":["eTJjZUp","iZMOHLs1"],"ua":"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36","dvm":4,"acw":"probably","pr":3,"ts_mtp":5,"tz":-120,"emd":"k:ai,vi,ao,vi d:defau,8f9de,defau,24476 g:4e652,f9991,defau,d5801","cookiesStr":"app.sig=_FqS3_SPN2D40Z4Qlq-_ybxzRvQ;datadome=VSGCNMYkY6L81typRJUpurlyd39dHxdfxIYc6DzRWDT7_rtZ8UodGcbow1okQR2zg_t4d_NcP0rC1ZHNz3XSDAGFZCJhFI49lai~x2Nb2unkhOlY8o4E04Wjmde~auDZ;app=eyJmbGFzaCI6e30sImNhY2hlZmxhc2giOltdLCJjc3JmU2VjcmV0IjoiLW9jbktLVXAzMjVTOEFEdFpLUU9kSXlMIn0=;policy=accepted;lang=fr;"}"""
|
test_data_json = """{"glvd":"Google Inc. (ARM)","glrd":"ANGLE (ARM, Mali-G57 MC2, OpenGL ES 3.2)","hc":8,"br_oh":745,"br_ow":393,
|
||||||
|
"br_h":745,"br_w":393,"rs_h":876,"rs_w":393,"rs_cd":24,"ars_h":876,"ars_w":393,"plg":0,"eva":33,
|
||||||
|
"vnd":"Google Inc.","plu":[],"ua":"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36","dvm":4,"acw":"probably",
|
||||||
|
"pr":2.75,"ts_mtp":5,"tz":-120,"emd":"k:ai,vi,ao",
|
||||||
|
"cookiesStr":"rskxRunCookie=0;datadome=GIfiRxEgyr5dwsnV0NoR9WPO3oA0CkMVLsqdvZLmuxHi81JFRbG7LaWKiAkSQRJTBkv3qBkGlCOC3uR00yZ9XSoCQFyE~1FcumXV8W_bgVPxfNzs6uQcZM8Ec61Q72UT;correlation_id=xlvzlpuwkh6syuqdfze05q7n41qle27qop4yjjvuifzapdo4npjlbi43uwbgle2m;ECOM_SESS=f2c2aihm0ocesmvx3dcs06ysh6;rCookie=0f3nn11uqq28ci5bqnveg5wme4bsr6e;lastRskxRun=1754748244881;x-xsrf-token=7ca984dc-6d35-4720-b77b-465392521d1b;__cf_bm=Szm2hZl9.PTO_DQF5DihrAOTq.dSmSrkKHsTqVenUxo-1754748237-1.0.1.1-gfLAB1wWYyHUb_scXGpXzAk6IkrkhvdNalhhsVhujY7xeFm02lwFKGUiTrBknONdwmvV3EKilTfiFORmJUDrjj3st4hoNdnrk0MT0SVed5w;OptanonConsent=isGpcEnabled=0&datestamp=Sat+Aug+09+2025+16%3A04%3A04+GMT%2B0200+(heure+d%E2%80%99%C3%A9t%C3%A9+d%E2%80%99Europe+centrale)&version=6.32.0&isIABGlobal=false&hosts=&landingPath=https%3A%2F%2Fwww.hermes.com%2Ffr%2Ffr%2Fproduct%2Fsac-p-tit-arcon-H085871CKAO%2F&groups=C0001%3A1%2CC0002%3A0%2CC0003%3A0%2CC0004%3A0;","model":"22041219PG",
|
||||||
|
"lgs":"[fr-FR,fr,en-US,en]",
|
||||||
|
"bchk":"3223aeb6721e0d0917e792818d193ac88dcd62fad5cad7bf7a2b2b473ecf58ee60f018dbdb1a5832e8dc6528387b0745971dbcd82387261e9a4e3f",
|
||||||
|
"nt_tcp":0,"nt_dns":0,"nt_rd":0,"nt_irt":-29.600000008940697,"nt_rt":1129,"nt_tls":6.700000002980232,"nt_ttf":1166.7000000029802,"nt_swt":22.900000005960464,
|
||||||
|
"nt_csd":411060,"nt_nhp":"h2","nt_rdc":0,"nt_it":"navigation","nt_prs":6.700000002980232,"nt_esc":0,"nt_ttrd":null,"nt_le":3.0999999940395355,"nt_dcle":141.79999999701977,
|
||||||
|
"nt_di":1415.2000000029802,"nt_dc":2862.6000000089407,"ccsB":"tags.js:1252:41 at nrWrapper (sac-p-tit-arcon-H085871CKAO.html:7:13468)",
|
||||||
|
"muev":false,"pro_t":false,"nhi":",,true,22041219PG,Android,14.0.0,128.0.6613.146,false","k_lyts":0,"k_lytk":"","serial":"fuljaueqguugf6pn"}"""
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_data_dict = json.loads(test_data_json)
|
test_data_dict = json.loads(test_data_json)
|
||||||
|
m_s_c = random.randint(0, 3)
|
||||||
|
m_c_c = random.randint(3, 5) # click count
|
||||||
|
m_m_c = random.randint(3, 5) # move count
|
||||||
|
m_cm_r = m_c_c / m_m_c
|
||||||
|
m_ms_r = -1 # move scroll ratio
|
||||||
|
if m_s_c == 0:
|
||||||
|
m_ms_r = -1
|
||||||
|
else:
|
||||||
|
m_ms_r = m_m_c / m_s_c
|
||||||
le_js_data = JsDataLeTypePojo(glrd=test_data_dict['glrd'], glvd=test_data_dict['glvd'], hc=test_data_dict['hc'],
|
le_js_data = JsDataLeTypePojo(glrd=test_data_dict['glrd'], glvd=test_data_dict['glvd'], hc=test_data_dict['hc'],
|
||||||
ua=test_data_dict['ua'], br_oh=test_data_dict['br_oh'], br_ow=test_data_dict['br_ow'],
|
ua=test_data_dict['ua'], br_oh=test_data_dict['br_oh'], br_ow=test_data_dict['br_ow'],
|
||||||
ars_h=test_data_dict['ars_h'], ars_w=test_data_dict['ars_w'], pr=test_data_dict['pr'],
|
ars_h=test_data_dict['ars_h'], ars_w=test_data_dict['ars_w'], pr=test_data_dict['pr'],
|
||||||
@@ -65,6 +105,23 @@ if __name__ == '__main__':
|
|||||||
plu=test_data_dict['plu'], vnd=test_data_dict['vnd'], dvm=test_data_dict['dvm'],
|
plu=test_data_dict['plu'], vnd=test_data_dict['vnd'], dvm=test_data_dict['dvm'],
|
||||||
ts_mtp=test_data_dict['ts_mtp'], eva=test_data_dict['eva'],
|
ts_mtp=test_data_dict['ts_mtp'], eva=test_data_dict['eva'],
|
||||||
rs_h=test_data_dict['rs_h'],
|
rs_h=test_data_dict['rs_h'],
|
||||||
rs_w=test_data_dict['rs_w'], rs_cd=test_data_dict['rs_cd'], m_s_c=0, m_m_c=3, m_c_c=3,
|
rs_w=test_data_dict['rs_w'], rs_cd=test_data_dict['rs_cd'],
|
||||||
m_cm_r=1, m_ms_r=-1, emd=test_data_dict['emd'])
|
m_s_c=m_s_c, m_m_c=m_m_c, m_c_c=m_c_c, m_cm_r=m_cm_r, m_ms_r=m_ms_r,
|
||||||
|
emd=test_data_dict['emd'],
|
||||||
|
bchk=test_data_dict['bchk'],
|
||||||
|
ccsB=test_data_dict['ccsB'],
|
||||||
|
nt_tcp=test_data_dict['nt_tcp'],
|
||||||
|
nt_dns=test_data_dict['nt_dns'],
|
||||||
|
nt_rd=test_data_dict['nt_rd'], nt_irt=test_data_dict['nt_irt'],
|
||||||
|
nt_rt=test_data_dict['nt_rt'],
|
||||||
|
nt_tls=test_data_dict['nt_tls'], nt_ttf=test_data_dict['nt_ttf'],
|
||||||
|
nt_swt=test_data_dict['nt_swt'],
|
||||||
|
nt_csd=test_data_dict['nt_csd'], nt_nhp=test_data_dict['nt_nhp'],
|
||||||
|
nt_rdc=test_data_dict['nt_rdc'],
|
||||||
|
nt_it=test_data_dict['nt_it'], nt_prs=test_data_dict['nt_prs'],
|
||||||
|
nt_esc=test_data_dict['nt_esc'],
|
||||||
|
nt_ttrd=test_data_dict['nt_ttrd'], nt_le=test_data_dict['nt_le'],
|
||||||
|
nt_dcle=test_data_dict['nt_dcle'], nt_di=test_data_dict['nt_di'],
|
||||||
|
nt_dc=test_data_dict['nt_dc'])
|
||||||
print(le_js_data.to_url_encoded_json())
|
print(le_js_data.to_url_encoded_json())
|
||||||
|
print(encrpte_to_jspl(le_js_data.to_url_encoded_json()))
|
||||||
|
|||||||
+224
-87
@@ -10,8 +10,19 @@ import jsonpickle
|
|||||||
@dataclass
|
@dataclass
|
||||||
class JsDataPojo:
|
class JsDataPojo:
|
||||||
# opts: str
|
# opts: str
|
||||||
ttst: int
|
r3n: int
|
||||||
ifov: bool
|
exp8: int
|
||||||
|
# uid: str
|
||||||
|
bci: bool
|
||||||
|
bcl: float
|
||||||
|
bct: str
|
||||||
|
bdt: str
|
||||||
|
dp0: bool
|
||||||
|
ucdv: bool
|
||||||
|
wdifrm: bool
|
||||||
|
iwgl: str
|
||||||
|
ttst: float
|
||||||
|
# ifov: bool
|
||||||
hc: int
|
hc: int
|
||||||
br_oh: int
|
br_oh: int
|
||||||
br_ow: int
|
br_ow: int
|
||||||
@@ -19,7 +30,12 @@ class JsDataPojo:
|
|||||||
wbd: bool
|
wbd: bool
|
||||||
tagpu: float
|
tagpu: float
|
||||||
wdif: bool
|
wdif: bool
|
||||||
wdifrm: bool
|
wdifpnh: int
|
||||||
|
lg: str
|
||||||
|
isb: bool
|
||||||
|
idp: bool
|
||||||
|
crt: int
|
||||||
|
|
||||||
npmtm: bool
|
npmtm: bool
|
||||||
br_h: int
|
br_h: int
|
||||||
br_w: int
|
br_w: int
|
||||||
@@ -31,7 +47,6 @@ class JsDataPojo:
|
|||||||
phe: bool
|
phe: bool
|
||||||
nm: bool
|
nm: bool
|
||||||
jsf: bool
|
jsf: bool
|
||||||
lg: str
|
|
||||||
pr: int
|
pr: int
|
||||||
ars_h: int
|
ars_h: int
|
||||||
ars_w: int
|
ars_w: int
|
||||||
@@ -109,7 +124,7 @@ class JsDataPojo:
|
|||||||
wdw: bool
|
wdw: bool
|
||||||
cokys: str
|
cokys: str
|
||||||
ecpc: bool
|
ecpc: bool
|
||||||
lgs: bool
|
lgs: list
|
||||||
lgsod: bool
|
lgsod: bool
|
||||||
psn: bool
|
psn: bool
|
||||||
edp: bool
|
edp: bool
|
||||||
@@ -136,69 +151,151 @@ class JsDataPojo:
|
|||||||
jset: int
|
jset: int
|
||||||
|
|
||||||
def __init__(self, glrd, glvd, hc, br_oh, ua, br_ow, br_h, br_w, rs_h, rs_w, rs_cd, ars_h, ars_w, plg, eva, plu,
|
def __init__(self, glrd, glvd, hc, br_oh, ua, br_ow, br_h, br_w, rs_h, rs_w, rs_cd, ars_h, ars_w, plg, eva, plu,
|
||||||
vnd, pr, ts_mtp, dvm, emd):
|
vnd, pr, ts_mtp, dvm, emd,
|
||||||
|
ccsB,
|
||||||
|
br_iw=513, br_ih=847, pf="Linux armv81", mob=True,
|
||||||
|
lgs=["fr-FR", "en-US", "zh-CN", "zh", "fr", "en"],
|
||||||
|
orf="debug",
|
||||||
|
bchk="3223aeb6721e0d0917e7928181193ac88dcd62fad5cadfbe7a2b2b473ecf58ee70f098dbdb1a1832e8dc6528387b0745971dbcd82384261e9a4e3f",
|
||||||
|
nt_tcp=0, nt_dns=0, nt_rd=0, nt_irt=-3.400000002235174, nt_rt=0, nt_tls=3.400000002235174,
|
||||||
|
nt_ttf=3.099999997764826, nt_swt=3.400000002235174, nt_csd=0, nt_nhp="", nt_rdc=0, nt_it="navigation",
|
||||||
|
nt_prs=0, nt_esc=-3.400000002235174, nt_ttrd=-2, nt_le=0, nt_dcle=0.10000000149011612,
|
||||||
|
nt_di=74.80000000074506, nt_dc=0, ccsT="""Error
|
||||||
|
at S (https://d.digital.hermes/tags.js:173:22)
|
||||||
|
at Un.C (https://d.digital.hermes/tags.js:1252:41
|
||||||
|
at nrWrapper (https://www.hermes.com/fr/fr/category/femme/Sacs%20et%20pochettes%20pour%20Femme%20_%20Herme%CC%80s%20France.html:7:13468)""",
|
||||||
|
ccsH="1050544242",
|
||||||
|
ccsV=",993b46baf0942a343b7e6b02fa3f8eef64727f077d3b0055af56e6994dcaf046", muev=False, prso=True,
|
||||||
|
nhi=",64,true,Nexus 5,Android,6.0,138.0.7204.184,false", k_lyts=48,
|
||||||
|
k_lytk="kg20va`l§'w8mh.71pdfoqcn[zy365x/\,-4bt9siu=j;r]e", tzp="Europe/Paris", isf2=False
|
||||||
|
):
|
||||||
# self.opts = "endpoint,ajaxListenerPath"
|
# self.opts = "endpoint,ajaxListenerPath"
|
||||||
self.ttst = (random.randint(5, 46) * 10 ** 16 + random.randint(1 * 10 ** 7, 9 * 10 ** 7)) / 10 ** 16
|
self.nddc = 1
|
||||||
self.ifov = False
|
self.r3n = 33
|
||||||
|
self.exp8 = 0
|
||||||
|
self.uid = None
|
||||||
|
self.bci = True # batteryManager.charging
|
||||||
|
self.bcl = 0.8 # batteryManager.level
|
||||||
|
self.bct = "Infinity" # batteryManager.chargingTime #to check
|
||||||
|
self.bdt = "Infinity" # batteryManager.chargingTime #to check
|
||||||
|
self.dp0 = False
|
||||||
|
self.ucdv = False
|
||||||
|
self.wdifrm = False
|
||||||
|
self.iwgl = "undefined" # to check
|
||||||
|
self.npmtm = False
|
||||||
|
self.wdif = False
|
||||||
|
self.wdifpnh = 2800984568
|
||||||
|
self.lg = "fr-FR"
|
||||||
|
self.isb = False # is brave
|
||||||
|
self.idp = True
|
||||||
|
self.crt = 0
|
||||||
|
self.vnd = vnd
|
||||||
|
self.bid = "NA"
|
||||||
|
self.med = "defined"
|
||||||
|
self.pltod = False
|
||||||
|
self.csssp = ""
|
||||||
|
self.awe = False
|
||||||
|
self.phe = False
|
||||||
|
self.dat = False
|
||||||
|
self.nm = False
|
||||||
|
self.geb = False
|
||||||
|
self.sqt = False
|
||||||
|
self.pf = pf
|
||||||
self.hc = hc
|
self.hc = hc
|
||||||
self.br_oh = br_oh
|
self.br_oh = br_oh
|
||||||
self.br_ow = br_ow
|
self.br_ow = br_ow
|
||||||
self.ua = ua
|
self.ua = ua
|
||||||
self.wbd = False
|
self.wbd = False
|
||||||
tag_pu = random.uniform(1, 10)
|
self.ts_mtp = ts_mtp
|
||||||
while len(str(tag_pu)) != 17:
|
self.mob = mob
|
||||||
tag_pu = random.uniform(1, 10)
|
self.lgs = lgs
|
||||||
self.tagpu = tag_pu
|
self.dvm = dvm
|
||||||
self.wdif = False
|
self.hcovdr = False
|
||||||
self.wdifrm = False
|
self.plovdr = False
|
||||||
self.npmtm = False
|
self.ftsovdr = False
|
||||||
self.br_h = br_h
|
self.orf = orf # to check
|
||||||
|
self.ttst = round(random.random(), 16)
|
||||||
self.br_w = br_w
|
self.br_w = br_w
|
||||||
self.isf = True
|
self.br_h = br_h
|
||||||
self.nddc = 1
|
self.br_iw = br_iw
|
||||||
self.rs_h = rs_h
|
self.br_ih = br_ih
|
||||||
self.rs_w = rs_w
|
|
||||||
self.rs_cd = rs_cd
|
|
||||||
self.phe = False
|
|
||||||
self.nm = False
|
|
||||||
self.jsf = False
|
|
||||||
self.lg = "fr-FR"
|
|
||||||
self.pr = pr
|
|
||||||
self.ars_h = ars_h
|
|
||||||
self.ars_w = ars_w
|
self.ars_w = ars_w
|
||||||
self.tz = -120
|
self.ars_h = ars_h
|
||||||
self.str_ss = True
|
self.rs_w = rs_w
|
||||||
self.str_ls = True
|
self.rs_h = rs_h
|
||||||
self.str_idb = True
|
self.rs_cd = rs_cd
|
||||||
self.str_odb = True
|
self.pr = pr
|
||||||
|
self.so = "portrait-primary"
|
||||||
|
self.vco = ""
|
||||||
|
self.vcots = False
|
||||||
|
self.vch = "probably"
|
||||||
|
self.vchts = True
|
||||||
|
self.vcw = "probably"
|
||||||
|
self.vcwts = True
|
||||||
|
self.vc3 = "maybe"
|
||||||
|
self.vc3ts = False
|
||||||
|
self.vcmp = ""
|
||||||
|
self.vcmpts = False
|
||||||
|
self.vcq = ""
|
||||||
|
self.vcqts = False
|
||||||
|
self.vc1 = "probably"
|
||||||
|
self.vc1ts = True
|
||||||
|
|
||||||
|
self.cssS = "3.93,0.39,12.24,9.75,5.64,10.98,13.46,2.19,6.51"
|
||||||
|
self.css0 = "7, 75, 0"
|
||||||
|
self.css1 = "9.74354, 0.354646, -0.00922698, 0.000685511, 0.203723, -5.61673, -0.755238, 0.0561098, -0.0279023, 0.481607, -10.9534, 0.813777, -0.375565, 6.48243, -147.433, 11.9534"
|
||||||
|
self.cssH = "15px"
|
||||||
|
|
||||||
|
self.plu = str.join(",", plu) if isinstance(plu, list) else plu
|
||||||
self.plgod = False
|
self.plgod = False
|
||||||
self.plg = plg
|
self.plg = plg
|
||||||
self.plgne = "err"
|
self.plgne = "NA"
|
||||||
self.plgre = "err"
|
self.plgre = "NA"
|
||||||
self.plgof = "err"
|
self.plgof = "NA"
|
||||||
self.plggt = "err"
|
self.plggt = "NA"
|
||||||
self.pltod = False
|
|
||||||
self.hcovdr = False
|
|
||||||
self.hcovdr2 = False
|
|
||||||
self.plovdr = False
|
|
||||||
self.plovdr2 = False
|
|
||||||
self.ftsovdr = False
|
|
||||||
self.ftsovdr2 = False
|
|
||||||
self.lb = False
|
|
||||||
self.eva = eva
|
|
||||||
self.lo = False
|
|
||||||
self.ts_mtp = ts_mtp
|
|
||||||
self.ts_tec = True
|
|
||||||
self.ts_tsa = True
|
|
||||||
self.vnd = vnd
|
|
||||||
self.bid = "NA"
|
|
||||||
self.mmt = "empty"
|
self.mmt = "empty"
|
||||||
self.plu = str.join(",", plu) if isinstance(plu, list) else plu
|
self.bchk = bchk
|
||||||
self.hdn = False
|
self.nt_tcp = nt_tcp
|
||||||
self.awe = False
|
self.nt_dns = nt_dns
|
||||||
self.geb = False
|
self.nt_rd = nt_rd
|
||||||
self.dat = False
|
self.nt_irt = nt_irt
|
||||||
self.med = "defined"
|
self.nt_rt = nt_rt
|
||||||
|
self.nt_tls = nt_tls
|
||||||
|
self.nt_ttf = nt_ttf
|
||||||
|
self.nt_swt = nt_swt
|
||||||
|
self.nt_csd = nt_csd
|
||||||
|
self.nt_nhp = nt_nhp
|
||||||
|
self.nt_rdc = nt_rdc
|
||||||
|
self.nt_it = nt_it
|
||||||
|
self.nt_prs = nt_prs
|
||||||
|
self.nt_esc = nt_esc
|
||||||
|
self.nt_ttrd = nt_ttrd
|
||||||
|
self.nt_le = nt_le
|
||||||
|
self.nt_dcle = nt_dcle
|
||||||
|
self.nt_di = nt_di
|
||||||
|
self.nt_dc = nt_dc
|
||||||
|
|
||||||
|
self.ccsT = ccsT
|
||||||
|
self.ccsB = ccsB
|
||||||
|
self.ccsH = ccsH
|
||||||
|
self.ccsV = ccsV
|
||||||
|
self.muev = muev
|
||||||
|
self.pro_t = True
|
||||||
|
self.wglo = True
|
||||||
|
self.prso = prso
|
||||||
|
self.wbst = True
|
||||||
|
self.psn = True
|
||||||
|
self.edp = True
|
||||||
|
self.addt = True
|
||||||
|
self.wsdc = True
|
||||||
|
self.ccsr = True
|
||||||
|
self.nuad = True
|
||||||
|
self.bcda = True
|
||||||
|
self.idn = True
|
||||||
|
self.capi = True
|
||||||
|
self.svde = True
|
||||||
|
self.vpbq = True
|
||||||
|
self.mq = "aptr:coarse, ahvr:none" # to check
|
||||||
self.aco = "probably"
|
self.aco = "probably"
|
||||||
self.acots = False
|
self.acots = False
|
||||||
self.acmp = "probably"
|
self.acmp = "probably"
|
||||||
@@ -220,35 +317,58 @@ class JsDataPojo:
|
|||||||
self.acwm = "maybe"
|
self.acwm = "maybe"
|
||||||
self.acwmts = False
|
self.acwmts = False
|
||||||
self.ocpt = False
|
self.ocpt = False
|
||||||
self.vco = ""
|
self.ckwa = False
|
||||||
self.vcots = False
|
self.spwn = False
|
||||||
self.vch = "probably"
|
self.emt = False
|
||||||
self.vchts = True
|
self.bfr = False
|
||||||
self.vcw = "probably"
|
self.tz = -120
|
||||||
self.vcwts = True
|
self.hdn = False
|
||||||
self.vc3 = "maybe"
|
self.xt1 = False # pdfViewerEnabled
|
||||||
self.vc3ts = False
|
self.cdhf = False # window.hasFocus()
|
||||||
self.vcmp = ""
|
self.eva = eva
|
||||||
self.vcmpts = False
|
self.cokys = ",loadTimes,csi,app"
|
||||||
self.vcq = ""
|
self.ecpc = False
|
||||||
self.vcqts = False
|
self.nhi = nhi
|
||||||
self.vc1 = "probably"
|
self.k_lyts = k_lyts
|
||||||
self.vc1ts = True
|
self.k_lytk = k_lytk
|
||||||
self.dvm = dvm
|
|
||||||
self.set_default_values()
|
|
||||||
self.emd = emd
|
self.emd = emd
|
||||||
|
self.wwl = False
|
||||||
self.glvd = glvd
|
self.glvd = glvd
|
||||||
self.glrd = glrd
|
self.glrd = glrd
|
||||||
self.wwl = False
|
self.tzp = tzp
|
||||||
|
self.isf = True
|
||||||
|
self.isf2 = isf2
|
||||||
|
self.dt = False
|
||||||
|
self.fph = 416468867
|
||||||
|
|
||||||
|
# self.ifov = False
|
||||||
|
# tag_pu = random.uniform(1, 10)
|
||||||
|
# while len(str(tag_pu)) != 17:
|
||||||
|
# tag_pu = random.uniform(1, 10)
|
||||||
|
# self.tagpu = tag_pu
|
||||||
|
# self.jsf = False
|
||||||
|
# self.str_ss = True
|
||||||
|
# self.str_ls = True
|
||||||
|
# self.str_idb = True
|
||||||
|
# self.str_odb = True
|
||||||
|
#
|
||||||
|
# # self.hcovdr2 = False
|
||||||
|
# # self.plovdr2 = False
|
||||||
|
# # self.ftsovdr2 = False
|
||||||
|
# self.lb = False
|
||||||
|
# self.lo = False
|
||||||
|
# self.ts_tec = True
|
||||||
|
# self.ts_tsa = True
|
||||||
|
|
||||||
|
# self.set_default_values()
|
||||||
|
|
||||||
self.jset = int(time.time())
|
self.jset = int(time.time())
|
||||||
|
|
||||||
def set_default_values(self):
|
def set_default_values(self):
|
||||||
self.sqt = False
|
self.sqt = False
|
||||||
self.so = "portrait-primary"
|
self.so = "portrait-primary"
|
||||||
self.wdw = True
|
self.wdw = True
|
||||||
self.cokys = "bG9hZFRpbWVzY3NpL="
|
self.lgs = ["fr-FR", "en-US", "zh-CN", "zh", "fr", "en"]
|
||||||
self.ecpc = False
|
|
||||||
self.lgs = True
|
|
||||||
self.lgsod = False
|
self.lgsod = False
|
||||||
self.psn = True
|
self.psn = True
|
||||||
self.edp = False
|
self.edp = False
|
||||||
@@ -262,21 +382,28 @@ class JsDataPojo:
|
|||||||
self.svde = False
|
self.svde = False
|
||||||
self.vpbq = True
|
self.vpbq = True
|
||||||
self.ucdv = False
|
self.ucdv = False
|
||||||
self.spwn = False
|
|
||||||
self.emt = False
|
|
||||||
self.bfr = False
|
|
||||||
self.dbov = False
|
self.dbov = False
|
||||||
self.cfpfe = "ZnVuY3Rpb24oKXt2YXIgbj10LGk9ZG9jdW1lbnRbJ1x4NzFceDc1XHg2NVx4NzJceDc5XHg1M1x4NjVceDZjXHg2NVx4NjNceDc0XHg2Zlx4NzInXShuKDQ2MSkpO2lmKGkpeyFmdW5jdGlvbiB0KCl7dHJ5e3ZhciBuPWlbJ1x4NzNceDY4XHg2MVx4NjRceDZmXHg3" # 4.40.0
|
self.cfpfe = "KHIsbixvLGEpPT57aWYoZVtyXSllW3JdLnB1c2gobik7ZWxzZXt2YXIgcyxjO2lmKHZvaWQgMCE9PW8pZm9yKHZhciB1PWRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCJzY3JpcHQiKSxkPTA7ZDx1Lmxlbmd0aDtkKyspe3ZhciBsPXVbZF07aWYobC5nZXRB" # 5.1.8
|
||||||
self.stcfp = "Ly9kLmRpZ2l0YWwuaGVybWVzL3RhZ3MuanM6Mjo4ODYzNSkKICAgIGF0IHQuZXhwb3J0cy5kZF9hZCAoaHR0cHM6Ly9kLmRpZ2l0YWwuaGVybWVzL3RhZ3MuanM6MjoxMDc3OTYpCiAgICBhdCBodHRwczovL2QuZGlnaXRhbC5oZXJtZXMvdGFncy5qczoyOjYzNzkw" # 4.40.0
|
self.stcfp = "JTIwSGVybWUlQ0MlODBzJTIwRnJhbmNlLmh0bTozNDoyNjY4OSkKYXRvIChmaWxlOi8vL1VzZXJzL3BhbmxlaS9Eb3dubG9hZHMvQnJhY2VsZXQlMjBDbGljJTIwSEglMjAtJTIwTWFycm9uJTIwXyUyMEhlcm1lJUNDJTgwcyUyMEZyYW5jZS5odG06MzQ6MzAzNzgp" # 5.1.8
|
||||||
self.ckwa = True
|
|
||||||
|
|
||||||
def to_url_encoded_json(self):
|
def to_url_encoded_json(self):
|
||||||
js_str = jsonpickle.encode(self, unpicklable=False).replace(": ", ":").replace(", ", ",")
|
js_str = jsonpickle.encode(self, unpicklable=False).replace(": ", ":").replace(", ", ",")
|
||||||
_safe_string = urllib.parse.quote(js_str)
|
# _safe_string = urllib.parse.quote(js_str)
|
||||||
return _safe_string
|
return js_str
|
||||||
|
|
||||||
|
|
||||||
test_data_json = """{"glvd":"ARM","glrd":"Mali-G57 MC2","hc":5,"br_oh":620,"br_ow":360,"br_h":620,"br_w":360,"rs_h":804,"rs_w":360,"rs_cd":24,"ars_h":804,"ars_w":360,"plg":2,"eva":33,"vnd":"Google Inc.","plu":["eTJjZUp","iZMOHLs1"],"ua":"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36","dvm":4,"acw":"probably","pr":3,"ts_mtp":5,"tz":-120,"emd":"k:ai,vi,ao,vi d:defau,8f9de,defau,24476 g:4e652,f9991,defau,d5801","cookiesStr":"app.sig=_FqS3_SPN2D40Z4Qlq-_ybxzRvQ;datadome=VSGCNMYkY6L81typRJUpurlyd39dHxdfxIYc6DzRWDT7_rtZ8UodGcbow1okQR2zg_t4d_NcP0rC1ZHNz3XSDAGFZCJhFI49lai~x2Nb2unkhOlY8o4E04Wjmde~auDZ;app=eyJmbGFzaCI6e30sImNhY2hlZmxhc2giOltdLCJjc3JmU2VjcmV0IjoiLW9jbktLVXAzMjVTOEFEdFpLUU9kSXlMIn0=;policy=accepted;lang=fr;"}"""
|
test_data_json = """{"glvd":"Google Inc. (ARM)","glrd":"ANGLE (ARM, Mali-G57 MC2, OpenGL ES 3.2)","hc":8,"br_oh":745,"br_ow":393,
|
||||||
|
"br_h":745,"br_w":393,"rs_h":876,"rs_w":393,"rs_cd":24,"ars_h":876,"ars_w":393,"plg":0,"eva":33,
|
||||||
|
"vnd":"Google Inc.","plu":[],"ua":"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36","dvm":4,"acw":"probably",
|
||||||
|
"pr":2.75,"ts_mtp":5,"tz":-120,"emd":"k:ai,vi,ao",
|
||||||
|
"cookiesStr":"rskxRunCookie=0;datadome=GIfiRxEgyr5dwsnV0NoR9WPO3oA0CkMVLsqdvZLmuxHi81JFRbG7LaWKiAkSQRJTBkv3qBkGlCOC3uR00yZ9XSoCQFyE~1FcumXV8W_bgVPxfNzs6uQcZM8Ec61Q72UT;correlation_id=xlvzlpuwkh6syuqdfze05q7n41qle27qop4yjjvuifzapdo4npjlbi43uwbgle2m;ECOM_SESS=f2c2aihm0ocesmvx3dcs06ysh6;rCookie=0f3nn11uqq28ci5bqnveg5wme4bsr6e;lastRskxRun=1754748244881;x-xsrf-token=7ca984dc-6d35-4720-b77b-465392521d1b;__cf_bm=Szm2hZl9.PTO_DQF5DihrAOTq.dSmSrkKHsTqVenUxo-1754748237-1.0.1.1-gfLAB1wWYyHUb_scXGpXzAk6IkrkhvdNalhhsVhujY7xeFm02lwFKGUiTrBknONdwmvV3EKilTfiFORmJUDrjj3st4hoNdnrk0MT0SVed5w;OptanonConsent=isGpcEnabled=0&datestamp=Sat+Aug+09+2025+16%3A04%3A04+GMT%2B0200+(heure+d%E2%80%99%C3%A9t%C3%A9+d%E2%80%99Europe+centrale)&version=6.32.0&isIABGlobal=false&hosts=&landingPath=https%3A%2F%2Fwww.hermes.com%2Ffr%2Ffr%2Fproduct%2Fsac-p-tit-arcon-H085871CKAO%2F&groups=C0001%3A1%2CC0002%3A0%2CC0003%3A0%2CC0004%3A0;","model":"22041219PG",
|
||||||
|
"lgs":"[fr-FR,fr,en-US,en]",
|
||||||
|
"bchk":"3223aeb6721e0d0917e792818d193ac88dcd62fad5cad7bf7a2b2b473ecf58ee60f018dbdb1a5832e8dc6528387b0745971dbcd82387261e9a4e3f",
|
||||||
|
"nt_tcp":0,"nt_dns":0,"nt_rd":0,"nt_irt":-29.600000008940697,"nt_rt":1129,"nt_tls":6.700000002980232,"nt_ttf":1166.7000000029802,"nt_swt":22.900000005960464,
|
||||||
|
"nt_csd":411060,"nt_nhp":"h2","nt_rdc":0,"nt_it":"navigation","nt_prs":6.700000002980232,"nt_esc":0,"nt_ttrd":null,"nt_le":3.0999999940395355,"nt_dcle":141.79999999701977,
|
||||||
|
"nt_di":1415.2000000029802,"nt_dc":2862.6000000089407,"ccsB":"tags.js:1252:41 at nrWrapper (sac-p-tit-arcon-H085871CKAO.html:7:13468)",
|
||||||
|
"muev":false,"pro_t":false,"nhi":",,true,22041219PG,Android,14.0.0,128.0.6613.146,false","k_lyts":0,"k_lytk":"","serial":"fuljaueqguugf6pn"}"""
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_data_dict = json.loads(test_data_json)
|
test_data_dict = json.loads(test_data_json)
|
||||||
@@ -286,7 +413,17 @@ if __name__ == '__main__':
|
|||||||
plg=test_data_dict['plg'], br_h=test_data_dict['br_h'], br_w=test_data_dict['br_w'],
|
plg=test_data_dict['plg'], br_h=test_data_dict['br_h'], br_w=test_data_dict['br_w'],
|
||||||
plu=test_data_dict['plu'], vnd=test_data_dict['vnd'], dvm=test_data_dict['dvm'],
|
plu=test_data_dict['plu'], vnd=test_data_dict['vnd'], dvm=test_data_dict['dvm'],
|
||||||
ts_mtp=test_data_dict['ts_mtp'], eva=test_data_dict['eva'], rs_h=test_data_dict['rs_h'],
|
ts_mtp=test_data_dict['ts_mtp'], eva=test_data_dict['eva'], rs_h=test_data_dict['rs_h'],
|
||||||
rs_w=test_data_dict['rs_w'], rs_cd=test_data_dict['rs_cd'], emd=test_data_dict['emd'])
|
rs_w=test_data_dict['rs_w'], rs_cd=test_data_dict['rs_cd'], emd=test_data_dict['emd'],
|
||||||
|
bchk=test_data_dict['bchk'],
|
||||||
|
ccsB=test_data_dict['ccsB'],
|
||||||
|
nt_tcp=test_data_dict['nt_tcp'],
|
||||||
|
nt_dns=test_data_dict['nt_dns'],
|
||||||
|
nt_rd=test_data_dict['nt_rd'], nt_irt=test_data_dict['nt_irt'], nt_rt=test_data_dict['nt_rt'],
|
||||||
|
nt_tls=test_data_dict['nt_tls'], nt_ttf=test_data_dict['nt_ttf'], nt_swt=test_data_dict['nt_swt'],
|
||||||
|
nt_csd=test_data_dict['nt_csd'], nt_nhp=test_data_dict['nt_nhp'], nt_rdc=test_data_dict['nt_rdc'],
|
||||||
|
nt_it=test_data_dict['nt_it'], nt_prs=test_data_dict['nt_prs'], nt_esc=test_data_dict['nt_esc'],
|
||||||
|
nt_ttrd=test_data_dict['nt_ttrd'], nt_le=test_data_dict['nt_le'],
|
||||||
|
nt_dcle=test_data_dict['nt_dcle'], nt_di=test_data_dict['nt_di'], nt_dc=test_data_dict['nt_dc'] )
|
||||||
print(js.to_url_encoded_json())
|
print(js.to_url_encoded_json())
|
||||||
# text_file = open("jsdata.txt", "w")
|
# text_file = open("jsdata.txt", "w")
|
||||||
# # write string to file
|
# # write string to file
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
from db.mongo_manager import MONGO_STORE_MANAGER
|
|
||||||
from excel_reader import read_contacts
|
|
||||||
from models.contact_pojo import ContactPojo
|
|
||||||
from queue_message.CookiesPublisher import CookiesPublisher, SHARED_OBJECT, TEST_QUEUE
|
|
||||||
from queue_message.appointmentrequestsender import AppointmentRequestSender
|
|
||||||
from queue_message.parallel_requestsender import ParallelRequestSender
|
|
||||||
from utiles import is_time_between
|
|
||||||
from utils.AppLogging import init_logger
|
|
||||||
from workers.proxies_constants import MOBILE_PROXY_LIST_FR
|
|
||||||
|
|
||||||
IPFIY = 'http://api.ipify.org'
|
|
||||||
NGROK_TEST = "https://bcc6-193-164-156-53.ngrok-free.app"
|
|
||||||
|
|
||||||
|
|
||||||
def is_already_sent(contact: ContactPojo) -> bool:
|
|
||||||
already_sent_contacts = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
|
||||||
for required_contact in already_sent_contacts:
|
|
||||||
if contact.mail == required_contact.email:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def filter_contacts(_contact_list: list) -> list:
|
|
||||||
already_sent_contacts = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
|
||||||
_link_to_validate_list = MONGO_STORE_MANAGER.get_links_to_validate()
|
|
||||||
_contact_list_to_book = []
|
|
||||||
for contact in _contact_list:
|
|
||||||
_to_add = True
|
|
||||||
for booked in already_sent_contacts:
|
|
||||||
if contact.mail == booked.email:
|
|
||||||
_to_add = False
|
|
||||||
# 如果已经收到链接了,就不要再请求
|
|
||||||
for link_to_validate in _link_to_validate_list:
|
|
||||||
if contact.mail == link_to_validate.email:
|
|
||||||
logger.info("{}: link already received".format(contact.mail))
|
|
||||||
_to_add = False
|
|
||||||
if _to_add:
|
|
||||||
_contact_list_to_book.append(contact)
|
|
||||||
|
|
||||||
return _contact_list_to_book
|
|
||||||
|
|
||||||
|
|
||||||
def is_open():
|
|
||||||
return is_time_between(datetime.time(10, 30), datetime.time(19, 00))
|
|
||||||
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
init_logger()
|
|
||||||
logger = logging.getLogger()
|
|
||||||
|
|
||||||
logger.addHandler(logging.StreamHandler(stream=sys.stdout))
|
|
||||||
|
|
||||||
|
|
||||||
def send_appointment_request(message_queue_name, _contact_list):
|
|
||||||
global count
|
|
||||||
count = count + 1
|
|
||||||
for _contact in _contact_list:
|
|
||||||
logger.info(_contact)
|
|
||||||
_cookiesPublisher = CookiesPublisher(queue_name=message_queue_name)
|
|
||||||
_cookiesPublisher.set_up_connection()
|
|
||||||
receiver = ParallelRequestSender(sub_contact_list=_contact_list, proxy_to_use_list=MOBILE_PROXY_LIST_FR,
|
|
||||||
queue_name=message_queue_name, just_send=True,
|
|
||||||
cookiesPublisher=_cookiesPublisher, logger=logger)
|
|
||||||
print("count is " + str(count))
|
|
||||||
receiver.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
contacts_file_path = '~/Desktop/31_03_to_test.xlsx'
|
|
||||||
_contact_list = read_contacts(contacts_file_path)[0:20]
|
|
||||||
_contact_list_to_book = filter_contacts(_contact_list)
|
|
||||||
_segment_number = 1
|
|
||||||
logger.info("{} contacts to book".format(len(_contact_list_to_book)))
|
|
||||||
last_thread = None
|
|
||||||
for i in range(0, _segment_number):
|
|
||||||
logger.info("segment is {}".format(i))
|
|
||||||
_step = int(len(_contact_list_to_book) / _segment_number)
|
|
||||||
_sublist = _contact_list_to_book[i * _step:_step * (i + 1)]
|
|
||||||
_thread1 = Thread(target=send_appointment_request, args=(TEST_QUEUE, _sublist))
|
|
||||||
last_thread = _thread1
|
|
||||||
_thread1.start()
|
|
||||||
last_thread.join()
|
|
||||||
@@ -3,6 +3,8 @@ import random
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from utils.iproayl_stick_proxy_list import FR_IPROYAL_STICKY_PROXY_LIST
|
||||||
|
|
||||||
FR_ASOCKS_MOBILE_PROXY = {
|
FR_ASOCKS_MOBILE_PROXY = {
|
||||||
'http': 'http://11797317-mobile-country-FR:nv958134x@190.2.151.110:14046',
|
'http': 'http://11797317-mobile-country-FR:nv958134x@190.2.151.110:14046',
|
||||||
'https': 'http://11797317-mobile-country-FR:nv958134x@190.2.151.110:14046',
|
'https': 'http://11797317-mobile-country-FR:nv958134x@190.2.151.110:14046',
|
||||||
@@ -54,17 +56,15 @@ FR_MOBILE_ANY_IP_ROTATING = {
|
|||||||
'http': 'http://user_6a7f21,type_residential,country_FR:d5c051@portal.anyip.io:1080',
|
'http': 'http://user_6a7f21,type_residential,country_FR:d5c051@portal.anyip.io:1080',
|
||||||
'https': 'http://user_6a7f21,type_residential,country_FR:d5c051@portal.anyip.io:1080',
|
'https': 'http://user_6a7f21,type_residential,country_FR:d5c051@portal.anyip.io:1080',
|
||||||
}
|
}
|
||||||
# 八分之一用data_impulse
|
FR_RES_IP_ROYAL_ROTATING = {
|
||||||
# MOBILE_PROXY_LIST = [FR_PROXY_MOB_OXY_STICKY, FR_PROXY_MOB_OXY_STICKY, FR_PROXY_MOB_OXY_STICKY, FR_PROXY_MOB_OXY_STICKY,
|
'http': 'http://Uv2qfG3PyhT6Wctw:V45HOlzAIssCYssJ_country-fr@geo.iproyal.com:12321',
|
||||||
# FR_PROXY_MOB_OXY_STICKY, FR_MOBILE_ANY_IP_STICKY, FR_MOBILE_ANY_IP_STICKY, FR_MOBILE_ANY_IP_STICKY,
|
'https': 'http://Uv2qfG3PyhT6Wctw:V45HOlzAIssCYssJ_country-fr@geo.iproyal.com:12321',
|
||||||
# FR_MOBILE_ANY_IP_STICKY, FR_MOBILE_ANY_IP_STICKY,
|
}
|
||||||
# FR_PROXY_RES_OXY_STICKY,
|
|
||||||
# FR_PROXY_DATA_IMPULSE_STICKY]
|
|
||||||
|
|
||||||
|
|
||||||
# MOBILE_PROXY_LIST = [FR_MOBILE_ANY_IP_STICKY, FR_PROXY_MOB_OXY_STICKY]
|
# MOBILE_PROXY_LIST = [FR_MOBILE_ANY_IP_STICKY, FR_PROXY_MOB_OXY_STICKY]
|
||||||
# MOBILE_PROXY_LIST = [FR_PROXY_MOB_OXY_STICKY]
|
MOBILE_PROXY_LIST = [FR_PROXY_MOB_OXY_STICKY]
|
||||||
MOBILE_PROXY_LIST = [FR_MOBILE_ANY_IP_STICKY]
|
# MOBILE_PROXY_LIST = [FR_MOBILE_ANY_IP_STICKY]
|
||||||
|
|
||||||
|
|
||||||
class ProxyManager:
|
class ProxyManager:
|
||||||
@@ -75,19 +75,25 @@ class ProxyManager:
|
|||||||
def get_link_validate_proxy(self, links_to_validate: list) -> list:
|
def get_link_validate_proxy(self, links_to_validate: list) -> list:
|
||||||
# return [FR_PROXY_RES_PARIS_OXY]
|
# return [FR_PROXY_RES_PARIS_OXY]
|
||||||
# return [FR_MOBILE_ANY_IP_ROTATING]
|
# return [FR_MOBILE_ANY_IP_ROTATING]
|
||||||
return [FR_PROXY_RES_OXY]
|
return [FR_RES_IP_ROYAL_ROTATING]
|
||||||
# if len(links_to_validate) > 15:
|
|
||||||
# return [FR_PROXY_RES_OXY, FR_PROXY_MOB_OXY, FR_PROXY_ASOCK_RES_2, FR_DATA_IMPULSE_RES]
|
|
||||||
# # return [FR_PROXY_RES_OXY, FR_PROXY_ASOCK_RES_2, FR_DATA_IMPULSE_RES, FR_ASOCKS_MOBILE_PROXY]
|
|
||||||
# else:
|
|
||||||
#
|
|
||||||
# return [FR_PROXY_RES_OXY, FR_PROXY_MOB_OXY, FR_PROXY_ASOCK_RES_2, FR_DATA_IMPULSE_RES]
|
|
||||||
# return [FR_PROXY_RES_OXY]
|
# return [FR_PROXY_RES_OXY]
|
||||||
|
|
||||||
|
def get_random_sticky_iproyal_proxy(self) -> dict:
|
||||||
|
_session_info = random.choice(FR_IPROYAL_STICKY_PROXY_LIST)
|
||||||
|
_proxy_template = {
|
||||||
|
'http': 'http://{}@geo.iproyal.com:12321',
|
||||||
|
'https': 'http://{}@geo.iproyal.com:12321',
|
||||||
|
}
|
||||||
|
_proxy_to_use = {}
|
||||||
|
_proxy_to_use["http"] = _proxy_template["http"].format(_session_info)
|
||||||
|
_proxy_to_use["https"] = _proxy_template["https"].format(_session_info)
|
||||||
|
return _proxy_to_use
|
||||||
|
|
||||||
def get_result_link_proxy(self):
|
def get_result_link_proxy(self):
|
||||||
return [FR_PROXY_RES_OXY, FR_PROXY_ASOCK_RES_2, FR_DATA_IMPULSE_RES, FR_ASOCKS_MOBILE_PROXY]
|
return [FR_PROXY_RES_OXY, FR_PROXY_ASOCK_RES_2, FR_DATA_IMPULSE_RES, FR_ASOCKS_MOBILE_PROXY]
|
||||||
|
|
||||||
def get_proxy_for_appointment_request(self) -> dict:
|
def get_proxy_for_appointment_request(self) -> dict:
|
||||||
|
return self.get_random_sticky_iproyal_proxy()
|
||||||
_chosen_proxy = random.choice(MOBILE_PROXY_LIST)
|
_chosen_proxy = random.choice(MOBILE_PROXY_LIST)
|
||||||
if "oxylabs" in _chosen_proxy["http"]:
|
if "oxylabs" in _chosen_proxy["http"]:
|
||||||
self.logger.info("use oxylabs proxy")
|
self.logger.info("use oxylabs proxy")
|
||||||
@@ -108,7 +114,9 @@ class ProxyManager:
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
_logger = logging.getLogger()
|
_logger = logging.getLogger()
|
||||||
_proxy = ProxyManager(logger=_logger)
|
_proxy = ProxyManager(logger=_logger)
|
||||||
_proxise = _proxy.get_proxy_for_appointment_request()
|
# _proxise = _proxy.get_random_sticky_iproyal_proxy()
|
||||||
|
# _proxise = _proxy.get_proxy_for_appointment_request()
|
||||||
|
_proxise = _proxy.get_link_validate_proxy([])[0]
|
||||||
print(_proxise)
|
print(_proxise)
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
"https://ip.oxylabs.io/location",
|
"https://ip.oxylabs.io/location",
|
||||||
|
|||||||
@@ -37,4 +37,8 @@ class CookiesPublisher:
|
|||||||
))
|
))
|
||||||
|
|
||||||
def message_count(self):
|
def message_count(self):
|
||||||
|
# try:
|
||||||
return self.channel.queue_declare(queue=self.to_queue, durable=True).method.message_count
|
return self.channel.queue_declare(queue=self.to_queue, durable=True).method.message_count
|
||||||
|
# except Exception as error:
|
||||||
|
# print(error)
|
||||||
|
# return 0
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ def is_open():
|
|||||||
return is_time_between(datetime.time(10, 30), datetime.time(19, 00))
|
return is_time_between(datetime.time(10, 30), datetime.time(19, 00))
|
||||||
|
|
||||||
|
|
||||||
|
def check_ms_mails(_mail_list_filtered):
|
||||||
|
print("check_ms_mails() called.")
|
||||||
|
# check_all_need_to_check_emails()
|
||||||
|
# for _mail in _mail_list_filtered:
|
||||||
|
# if "outlook.com" in _mail.mail or "hotmail.com" in _mail.mail:
|
||||||
|
# check_mail(_mail.mail)
|
||||||
|
|
||||||
|
|
||||||
def get_xsfr_token_from_cookies(cookies_str: str) -> Optional[str]:
|
def get_xsfr_token_from_cookies(cookies_str: str) -> Optional[str]:
|
||||||
_simple_cookies = SimpleCookie()
|
_simple_cookies = SimpleCookie()
|
||||||
_simple_cookies.load(cookies_str)
|
_simple_cookies.load(cookies_str)
|
||||||
@@ -122,9 +130,18 @@ class AppointmentRequestSender(threading.Thread):
|
|||||||
ars_h=_received_dict['ars_h'], ars_w=_received_dict['ars_w'], pr=_received_dict['pr'],
|
ars_h=_received_dict['ars_h'], ars_w=_received_dict['ars_w'], pr=_received_dict['pr'],
|
||||||
plg=_received_dict['plg'], br_h=_received_dict['br_h'], br_w=_received_dict['br_w'],
|
plg=_received_dict['plg'], br_h=_received_dict['br_h'], br_w=_received_dict['br_w'],
|
||||||
plu=_received_dict['plu'], vnd=_received_dict['vnd'], dvm=_received_dict['dvm'],
|
plu=_received_dict['plu'], vnd=_received_dict['vnd'], dvm=_received_dict['dvm'],
|
||||||
ts_mtp=_received_dict['ts_mtp'], eva=_received_dict['eva'],
|
ts_mtp=_received_dict['ts_mtp'], eva=_received_dict['eva'], rs_h=_received_dict['rs_h'],
|
||||||
rs_h=_received_dict['rs_h'],
|
rs_w=_received_dict['rs_w'], rs_cd=_received_dict['rs_cd'], emd=_received_dict['emd'],
|
||||||
rs_w=_received_dict['rs_w'], rs_cd=_received_dict['rs_cd'], emd=_received_dict['emd'])
|
bchk=_received_dict['bchk'],
|
||||||
|
ccsB=_received_dict['ccsB'],
|
||||||
|
nt_tcp=_received_dict['nt_tcp'],
|
||||||
|
nt_dns=_received_dict['nt_dns'],
|
||||||
|
nt_rd=_received_dict['nt_rd'], nt_irt=_received_dict['nt_irt'], nt_rt=_received_dict['nt_rt'],
|
||||||
|
nt_tls=_received_dict['nt_tls'], nt_ttf=_received_dict['nt_ttf'], nt_swt=_received_dict['nt_swt'],
|
||||||
|
nt_csd=_received_dict['nt_csd'], nt_nhp=_received_dict['nt_nhp'], nt_rdc=_received_dict['nt_rdc'],
|
||||||
|
nt_it=_received_dict['nt_it'], nt_prs=_received_dict['nt_prs'], nt_esc=_received_dict['nt_esc'],
|
||||||
|
nt_ttrd=_received_dict['nt_ttrd'], nt_le=_received_dict['nt_le'],
|
||||||
|
nt_dcle=_received_dict['nt_dcle'], nt_di=_received_dict['nt_di'], nt_dc=_received_dict['nt_dc'] )
|
||||||
_received_cookies = _received_dict["cookiesStr"]
|
_received_cookies = _received_dict["cookiesStr"]
|
||||||
# remove already sent contacts
|
# remove already sent contacts
|
||||||
self.contact_list = filter_contacts(self.contact_list, self.already_tried_contact_list)
|
self.contact_list = filter_contacts(self.contact_list, self.already_tried_contact_list)
|
||||||
@@ -169,21 +186,25 @@ class AppointmentRequestSender(threading.Thread):
|
|||||||
else:
|
else:
|
||||||
m_ms_r = m_m_c / m_s_c
|
m_ms_r = m_m_c / m_s_c
|
||||||
|
|
||||||
js_le_data = JsDataLeTypePojo(glrd=_received_dict['glrd'], glvd=_received_dict['glvd'],
|
js_le_data = JsDataLeTypePojo(glrd=_received_dict['glrd'], glvd=_received_dict['glvd'], hc=_received_dict['hc'],
|
||||||
hc=_received_dict['hc'],
|
ua=_received_dict['ua'], br_oh=_received_dict['br_oh'], br_ow=_received_dict['br_ow'],
|
||||||
ua=_received_dict['ua'], br_oh=_received_dict['br_oh'],
|
ars_h=_received_dict['ars_h'], ars_w=_received_dict['ars_w'], pr=_received_dict['pr'],
|
||||||
br_ow=_received_dict['br_ow'],
|
plg=_received_dict['plg'], br_h=_received_dict['br_h'], br_w=_received_dict['br_w'],
|
||||||
ars_h=_received_dict['ars_h'], ars_w=_received_dict['ars_w'],
|
plu=_received_dict['plu'], vnd=_received_dict['vnd'], dvm=_received_dict['dvm'],
|
||||||
pr=_received_dict['pr'],
|
ts_mtp=_received_dict['ts_mtp'], eva=_received_dict['eva'], rs_h=_received_dict['rs_h'],
|
||||||
plg=_received_dict['plg'], br_h=_received_dict['br_h'],
|
rs_w=_received_dict['rs_w'], rs_cd=_received_dict['rs_cd'], emd=_received_dict['emd'],
|
||||||
br_w=_received_dict['br_w'],
|
bchk=_received_dict['bchk'],
|
||||||
plu=_received_dict['plu'], vnd=_received_dict['vnd'],
|
ccsB=_received_dict['ccsB'],
|
||||||
dvm=_received_dict['dvm'],
|
nt_tcp=_received_dict['nt_tcp'],
|
||||||
ts_mtp=_received_dict['ts_mtp'], eva=_received_dict['eva'],
|
nt_dns=_received_dict['nt_dns'],
|
||||||
rs_h=_received_dict['rs_h'],
|
nt_rd=_received_dict['nt_rd'], nt_irt=_received_dict['nt_irt'], nt_rt=_received_dict['nt_rt'],
|
||||||
rs_w=_received_dict['rs_w'], rs_cd=_received_dict['rs_cd'],
|
nt_tls=_received_dict['nt_tls'], nt_ttf=_received_dict['nt_ttf'], nt_swt=_received_dict['nt_swt'],
|
||||||
|
nt_csd=_received_dict['nt_csd'], nt_nhp=_received_dict['nt_nhp'], nt_rdc=_received_dict['nt_rdc'],
|
||||||
|
nt_it=_received_dict['nt_it'], nt_prs=_received_dict['nt_prs'], nt_esc=_received_dict['nt_esc'],
|
||||||
|
nt_ttrd=_received_dict['nt_ttrd'], nt_le=_received_dict['nt_le'],
|
||||||
|
nt_dcle=_received_dict['nt_dcle'], nt_di=_received_dict['nt_di'], nt_dc=_received_dict['nt_dc'],
|
||||||
m_s_c=m_s_c, m_m_c=m_m_c, m_c_c=m_c_c,
|
m_s_c=m_s_c, m_m_c=m_m_c, m_c_c=m_c_c,
|
||||||
m_cm_r=m_cm_r, m_ms_r=m_ms_r, emd=_received_dict['emd'])
|
m_cm_r=m_cm_r, m_ms_r=m_ms_r)
|
||||||
time.sleep(random.randint(1, 4))
|
time.sleep(random.randint(1, 4))
|
||||||
_new_le_cookies = captchaResultGetter.get_le_valid_cookie(proxy_to_use=_proxy_to_use,
|
_new_le_cookies = captchaResultGetter.get_le_valid_cookie(proxy_to_use=_proxy_to_use,
|
||||||
js_le_type_data=js_le_data,
|
js_le_type_data=js_le_data,
|
||||||
@@ -264,6 +285,7 @@ class AppointmentRequestSender(threading.Thread):
|
|||||||
self.logger.info("will get mail from " + mail.mail)
|
self.logger.info("will get mail from " + mail.mail)
|
||||||
_mail_list_filtered.append(mail)
|
_mail_list_filtered.append(mail)
|
||||||
self.logger.info("will call find_links_to_validate_from_mail_list, size = " + str(len(_mail_list_filtered)))
|
self.logger.info("will call find_links_to_validate_from_mail_list, size = " + str(len(_mail_list_filtered)))
|
||||||
|
check_ms_mails(_mail_list_filtered)
|
||||||
find_links_to_validate_from_mail_list(_mail_list_filtered, self.logger)
|
find_links_to_validate_from_mail_list(_mail_list_filtered, self.logger)
|
||||||
self.already_read_emails = True
|
self.already_read_emails = True
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import json
|
|
||||||
import random
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
from concurrent.futures.thread import ThreadPoolExecutor
|
|
||||||
|
|
||||||
import pika
|
|
||||||
|
|
||||||
from db.mongo_manager import MONGO_STORE_MANAGER
|
|
||||||
from mail.mail_reader_all_contacts import find_links_to_validate_from_mail_list
|
|
||||||
from models.ReserveResultPojo import ReserveResultPojo
|
|
||||||
from models.contact_pojo import ContactPojo
|
|
||||||
from models.jsdata_le_pojo import JsDataLeTypePojo
|
|
||||||
from models.jsdata_pojo import JsDataPojo
|
|
||||||
from models.result_pojo import RequestResult
|
|
||||||
from queue_message.CookiesPublisher import CookiesPublisher
|
|
||||||
from queue_message.appointmentrequestsender import filter_contacts, is_open
|
|
||||||
from utiles import is_time_between
|
|
||||||
from workers.captcha_result_getter import CaptchaResultGetter, HERMES_REGISTER
|
|
||||||
from workers.sender import Sender
|
|
||||||
|
|
||||||
QUEUE_HOST = "appointment.lpaconsulting.fr"
|
|
||||||
REQUEST_DATA_QUEUE = 'REQUEST_DATA'
|
|
||||||
credentials = pika.PlainCredentials('appointment', 'ZyuhJZ2xEYWhElhpJjy7YEpZGZwNYJz2fHIu')
|
|
||||||
|
|
||||||
|
|
||||||
def is_already_sent(contact: ContactPojo) -> bool:
|
|
||||||
already_sent_contacts = MONGO_STORE_MANAGER.get_all_successful_items_for_day()
|
|
||||||
for required_contact in already_sent_contacts:
|
|
||||||
if contact.mail == required_contact.email:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class ParallelRequestSender(threading.Thread):
|
|
||||||
def __init__(self, sub_contact_list: list, proxy_to_use_list, logger, cookiesPublisher: CookiesPublisher,
|
|
||||||
just_send=False,
|
|
||||||
queue_name=REQUEST_DATA_QUEUE):
|
|
||||||
super().__init__()
|
|
||||||
self.connection = None
|
|
||||||
self.just_send = just_send
|
|
||||||
self.logger = logger
|
|
||||||
self.already_tried_contact_list = []
|
|
||||||
self.cookiesPublisher = cookiesPublisher
|
|
||||||
self.channel = None
|
|
||||||
self.valid_csrf = None
|
|
||||||
self.list_to_retrieve_mails = sub_contact_list
|
|
||||||
self.contact_list = sub_contact_list
|
|
||||||
self.queue_name = queue_name
|
|
||||||
self.proxy_to_use_list = proxy_to_use_list
|
|
||||||
self.already_read_emails = False
|
|
||||||
|
|
||||||
def set_up_connection(self):
|
|
||||||
self.connection = pika.BlockingConnection(
|
|
||||||
pika.ConnectionParameters(host=QUEUE_HOST, port=5672, credentials=credentials))
|
|
||||||
self.channel = self.connection.channel()
|
|
||||||
|
|
||||||
def listen_to_queue(self, callback):
|
|
||||||
self.logger.info("listen to queue {}".format(self.queue_name))
|
|
||||||
self.channel.basic_qos(prefetch_count=1)
|
|
||||||
self.channel.basic_consume(queue=self.queue_name, auto_ack=False, on_message_callback=callback)
|
|
||||||
self.channel.start_consuming()
|
|
||||||
|
|
||||||
def send_request(self, _received_cookies, _received_dict, js_data: JsDataPojo, logger,
|
|
||||||
_contact) -> RequestResult:
|
|
||||||
_proxy_to_use = self.generate_proxy()
|
|
||||||
logger.info("send_request for contact: {}, cookies: {}".format(_contact.mail, _received_cookies))
|
|
||||||
logger.info("proxy to use is {}".format(_proxy_to_use))
|
|
||||||
sender = Sender(_received_cookies, cookiesPublisher=self.cookiesPublisher, received_dict=_received_dict,
|
|
||||||
proxy_to_use=_proxy_to_use, logger=logger)
|
|
||||||
# remove already sent contacts
|
|
||||||
if is_open():
|
|
||||||
captchaResultGetter = CaptchaResultGetter()
|
|
||||||
_new_cookies = captchaResultGetter.get_valid_ch_cookie(sender.proxy_to_use, js_data,
|
|
||||||
old_valid_cookie=_received_cookies)
|
|
||||||
# self.contact_list = filter_contacts(self.contact_list)
|
|
||||||
logger.info(_contact.mail)
|
|
||||||
valid_csrf = captchaResultGetter.get_csrf(
|
|
||||||
proxy_to_use=_proxy_to_use, js_data=js_data,
|
|
||||||
cookie=_new_cookies)
|
|
||||||
if isinstance(valid_csrf, str):
|
|
||||||
if _new_cookies is not None:
|
|
||||||
logger.info("new cookie is " + _new_cookies)
|
|
||||||
# m_s_c = f.scroll
|
|
||||||
m_s_c = random.randint(0, 3)
|
|
||||||
m_c_c = random.randint(3, 5) # click count
|
|
||||||
m_m_c = random.randint(3, 5) # move count
|
|
||||||
m_cm_r = m_c_c / m_m_c
|
|
||||||
m_ms_r = random.randint(-1, 1)
|
|
||||||
|
|
||||||
js_le_data = JsDataLeTypePojo(glrd=_received_dict['glrd'], glvd=_received_dict['glvd'],
|
|
||||||
hc=_received_dict['hc'],
|
|
||||||
ua=_received_dict['ua'], br_oh=_received_dict['br_oh'],
|
|
||||||
br_ow=_received_dict['br_ow'],
|
|
||||||
ars_h=_received_dict['ars_h'], ars_w=_received_dict['ars_w'],
|
|
||||||
pr=_received_dict['pr'],
|
|
||||||
plg=_received_dict['plg'], br_h=_received_dict['br_h'],
|
|
||||||
br_w=_received_dict['br_w'],
|
|
||||||
plu=_received_dict['plu'], vnd=_received_dict['vnd'],
|
|
||||||
dvm=_received_dict['dvm'],
|
|
||||||
ts_mtp=_received_dict['ts_mtp'], eva=_received_dict['eva'],
|
|
||||||
rs_h=_received_dict['rs_h'],
|
|
||||||
rs_w=_received_dict['rs_w'], rs_cd=_received_dict['rs_cd'],
|
|
||||||
m_s_c=m_s_c, m_m_c=m_m_c, m_c_c=m_c_c,
|
|
||||||
m_cm_r=m_cm_r, m_ms_r=m_ms_r, emd=_received_dict['emd'])
|
|
||||||
time.sleep(random.randint(1, 4))
|
|
||||||
_new_le_cookies = captchaResultGetter.get_le_valid_cookie(proxy_to_use=_proxy_to_use,
|
|
||||||
js_le_type_data=js_le_data,
|
|
||||||
old_valid_cookie=_new_cookies)
|
|
||||||
if _new_le_cookies is not None:
|
|
||||||
# self.logger.info("new le type cookie is " + _new_le_cookies)
|
|
||||||
sender.cookie_str = _new_le_cookies
|
|
||||||
time.sleep(random.randint(1, 3))
|
|
||||||
self.already_tried_contact_list.append(_contact)
|
|
||||||
can_continue = sender.send_request(HERMES_REGISTER, js_data, _contact, csrf=valid_csrf)
|
|
||||||
if can_continue == RequestResult.SUCCESS:
|
|
||||||
# 让服务器读取成功的约会
|
|
||||||
try:
|
|
||||||
self.logger.info("try to remove success contact from list to retrieve mails")
|
|
||||||
self.list_to_retrieve_mails.remove(_contact)
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.info(
|
|
||||||
"exception while remove success contact from list to retrieve mails")
|
|
||||||
print(e)
|
|
||||||
else:
|
|
||||||
can_continue = RequestResult.COOKIES_ERROR
|
|
||||||
else:
|
|
||||||
can_continue = RequestResult.COOKIES_ERROR
|
|
||||||
if can_continue == RequestResult.BLOCKED:
|
|
||||||
self.logger.info("cannot continue, we are blocked " + str(self.valid_csrf))
|
|
||||||
elif can_continue == RequestResult.PROXY_ERROR:
|
|
||||||
self.logger.info("PROXY_ERROR, will not reset valid_csrf")
|
|
||||||
elif can_continue == RequestResult.COOKIES_ERROR:
|
|
||||||
self.logger.info("COOKIES_ERROR, will not reset valid_csrf")
|
|
||||||
else:
|
|
||||||
self.logger.info("can continue, will reset valid_csrf")
|
|
||||||
self.valid_csrf = None
|
|
||||||
return can_continue
|
|
||||||
else:
|
|
||||||
return valid_csrf
|
|
||||||
# return RequestResult.CTRF_ERROR
|
|
||||||
|
|
||||||
def getChTypeJsDataFromDict(self, _received_dict) -> JsDataPojo:
|
|
||||||
return JsDataPojo(glrd=_received_dict['glrd'], glvd=_received_dict['glvd'], hc=_received_dict['hc'],
|
|
||||||
ua=_received_dict['ua'], br_oh=_received_dict['br_oh'], br_ow=_received_dict['br_ow'],
|
|
||||||
ars_h=_received_dict['ars_h'], ars_w=_received_dict['ars_w'], pr=_received_dict['pr'],
|
|
||||||
plg=_received_dict['plg'], br_h=_received_dict['br_h'], br_w=_received_dict['br_w'],
|
|
||||||
plu=_received_dict['plu'], vnd=_received_dict['vnd'], dvm=_received_dict['dvm'],
|
|
||||||
ts_mtp=_received_dict['ts_mtp'], eva=_received_dict['eva'],
|
|
||||||
rs_h=_received_dict['rs_h'],
|
|
||||||
rs_w=_received_dict['rs_w'], rs_cd=_received_dict['rs_cd'], emd=_received_dict['emd'])
|
|
||||||
|
|
||||||
def generate_proxy(self):
|
|
||||||
_port = random.randint(40001, 49999)
|
|
||||||
_chosen_proxy = random.choice(self.proxy_to_use_list)
|
|
||||||
self.logger.info("generated port is {}".format(_port))
|
|
||||||
_proxy_to_use = {}
|
|
||||||
_proxy_to_use["http"] = _chosen_proxy["http"].format(_port)
|
|
||||||
_proxy_to_use["https"] = _chosen_proxy["https"].format(_port)
|
|
||||||
return _proxy_to_use
|
|
||||||
|
|
||||||
def on_message(self, ch, method, properties, body):
|
|
||||||
_message_count = self.cookiesPublisher.message_count()
|
|
||||||
self.logger.info("message count in queue is {}".format(_message_count))
|
|
||||||
# prepare the contact list
|
|
||||||
if self.just_send:
|
|
||||||
self.contact_list = filter_contacts(self.contact_list, self.already_tried_contact_list)
|
|
||||||
else:
|
|
||||||
self.contact_list = filter_contacts(self.contact_list)
|
|
||||||
# remove already booked contacts
|
|
||||||
random.shuffle(self.contact_list)
|
|
||||||
_received_object = body.decode("UTF-8")
|
|
||||||
self.logger.info(f" [x] Received {_received_object}")
|
|
||||||
step = 5
|
|
||||||
_received_dict = json.loads(_received_object)
|
|
||||||
js_data = self.getChTypeJsDataFromDict(_received_dict)
|
|
||||||
_received_cookies = _received_dict["cookiesStr"]
|
|
||||||
if len(self.contact_list) > step:
|
|
||||||
_sub_list = self.contact_list[0:step]
|
|
||||||
result = None
|
|
||||||
for con in _sub_list:
|
|
||||||
with ThreadPoolExecutor(max_workers=step) as executor:
|
|
||||||
result = executor.submit(self.send_request, _received_cookies, _received_dict, js_data, self.logger,
|
|
||||||
con)
|
|
||||||
self.logger.info("result is: " + str(result.result()))
|
|
||||||
if result.result() == RequestResult.SUCCESS:
|
|
||||||
self.logger.info("Success for {}, with cookies{}".format(con.mail, _received_cookies))
|
|
||||||
if result.result() == RequestResult.BLOCKED or result.result() == RequestResult.CTRF_ERROR:
|
|
||||||
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
||||||
|
|
||||||
else:
|
|
||||||
ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True)
|
|
||||||
else:
|
|
||||||
self.retrieve_invalidate_urls()
|
|
||||||
self.logger.info("empty list")
|
|
||||||
time.sleep(120)
|
|
||||||
self.logger.info("will basic_reject method.delivery_tag: " + str(method.delivery_tag))
|
|
||||||
ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.logger.info(threading.currentThread().name + " starts")
|
|
||||||
self.set_up_connection()
|
|
||||||
self.listen_to_queue(self.on_message)
|
|
||||||
self.channel.start_consuming()
|
|
||||||
|
|
||||||
def retrieve_invalidate_urls(self):
|
|
||||||
if not self.already_read_emails and len(self.list_to_retrieve_mails) > 0:
|
|
||||||
self.logger.info("will retrieve validate urls")
|
|
||||||
time.sleep(30)
|
|
||||||
_mail_list = MONGO_STORE_MANAGER.get_destination_emails()
|
|
||||||
_mail_list_filtered = []
|
|
||||||
for mail in _mail_list:
|
|
||||||
for _contact in self.list_to_retrieve_mails:
|
|
||||||
if _contact.mail == mail.mail:
|
|
||||||
_mail_list_filtered.append(mail)
|
|
||||||
self.logger.info("will call find_links_to_validate_from_mail_list, size = " + str(len(_mail_list_filtered)))
|
|
||||||
find_links_to_validate_from_mail_list(_mail_list_filtered)
|
|
||||||
self.already_read_emails = True
|
|
||||||
else:
|
|
||||||
self.logger.info("already read emails, will not retrieve validate urls")
|
|
||||||
@@ -10,9 +10,7 @@ from models.contact_pojo import ContactPojo
|
|||||||
from queue_message.CookiesPublisher import CookiesPublisher, SHARED_OBJECT, TEST_QUEUE, MORNING_DATA_CACHE, \
|
from queue_message.CookiesPublisher import CookiesPublisher, SHARED_OBJECT, TEST_QUEUE, MORNING_DATA_CACHE, \
|
||||||
MORNING_DATA_CACHE_2, MORNING_DATA_CACHE_BAK
|
MORNING_DATA_CACHE_2, MORNING_DATA_CACHE_BAK
|
||||||
from queue_message.appointmentrequestsender import AppointmentRequestSender
|
from queue_message.appointmentrequestsender import AppointmentRequestSender
|
||||||
from utiles import is_time_between
|
|
||||||
from utils.AppLogging import init_logger
|
from utils.AppLogging import init_logger
|
||||||
from workers.proxies_constants import MOBILE_PROXY_LIST_FR
|
|
||||||
|
|
||||||
|
|
||||||
def is_already_sent(contact: ContactPojo) -> bool:
|
def is_already_sent(contact: ContactPojo) -> bool:
|
||||||
@@ -101,8 +99,9 @@ def send_request_for_file_list(file_list: list, thread_number: int = 20, data_qu
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# file_list = ['~/Desktop/contact_list_2024-05-23.xlsx',
|
# file_list = ['~/Desktop/contact_list_2024-05-23.xlsx',
|
||||||
# '~/Desktop/contact_list_2024-05-21.xlsx',
|
# '~/Desktop/contact_list_2024-05-21.xlsx',
|
||||||
# '~/Desktop/15_05_to_test.xlsx']
|
# file_list = ['~/Desktop/contact_list_2025-10-30.xlsx']
|
||||||
# file_list = ['~/Desktop/15_05_to_test.xlsx', '~/Desktop/16_05_to_test.xlsx']
|
file_list = ['~/Desktop/contact_list_2025-11-28.xlsx']
|
||||||
file_list = ['~/Desktop/contact_list_2025-06-09_2.xlsx']
|
# file_list = ['~/Desktop/contact_list_2025-11-06.xlsx']
|
||||||
send_request_for_file_list(file_list=file_list, thread_number=10,
|
# file_list = ['~/Desktop/contact_list_all.xlsx']
|
||||||
|
send_request_for_file_list(file_list=file_list, thread_number=40,
|
||||||
data_queue_name=MORNING_DATA_CACHE, stop_at_hour=19, stop_at_mins=50)
|
data_queue_name=MORNING_DATA_CACHE, stop_at_hour=19, stop_at_mins=50)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
APScheduler==3.10.4
|
||||||
|
curl_cffi==0.7.1
|
||||||
|
openpyxl
|
||||||
|
pika
|
||||||
|
schedule
|
||||||
|
python-dotenv
|
||||||
|
PySocks
|
||||||
|
imapclient
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
|
||||||
|
from apscheduler.schedulers.blocking import BlockingScheduler
|
||||||
|
|
||||||
|
from link_validator_executor import start_link_validation
|
||||||
|
|
||||||
|
|
||||||
|
def start_check_results_job(sched):
|
||||||
|
sched.add_job(start_link_validation, 'cron', day_of_week='mon-sun', hour='14',
|
||||||
|
minute='10',
|
||||||
|
misfire_grace_time=10,
|
||||||
|
second='10', timezone='Europe/Paris', max_instances=1, args=[])
|
||||||
|
|
||||||
|
|
||||||
|
def config_and_start_jobs():
|
||||||
|
executors = {
|
||||||
|
'default': ThreadPoolExecutor(1),
|
||||||
|
'processpool': ProcessPoolExecutor(1)
|
||||||
|
}
|
||||||
|
sched = BlockingScheduler(executors=executors)
|
||||||
|
start_check_results_job(sched)
|
||||||
|
sched.print_jobs()
|
||||||
|
sched.start()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
config_and_start_jobs()
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
|
||||||
|
from apscheduler.schedulers.blocking import BlockingScheduler
|
||||||
|
|
||||||
|
from queue_message.CookiesPublisher import MORNING_DATA_CACHE
|
||||||
|
from request_sender_test import send_request_for_file_list
|
||||||
|
|
||||||
|
|
||||||
|
def start_book_appointment():
|
||||||
|
file_list = ['~/Desktop/contact_list_2025-10-30.xlsx']
|
||||||
|
send_request_for_file_list(file_list=file_list, thread_number=2,
|
||||||
|
data_queue_name=MORNING_DATA_CACHE, stop_at_hour=18, stop_at_mins=20)
|
||||||
|
|
||||||
|
|
||||||
|
def start_check_results_job(sched):
|
||||||
|
sched.add_job(start_book_appointment, 'cron', day_of_week='mon-sat', hour='11',
|
||||||
|
minute='40',
|
||||||
|
misfire_grace_time=10,
|
||||||
|
second='10', timezone='Europe/Paris', max_instances=1, args=[])
|
||||||
|
|
||||||
|
|
||||||
|
def config_and_start_jobs():
|
||||||
|
executors = {
|
||||||
|
'default': ThreadPoolExecutor(30),
|
||||||
|
'processpool': ProcessPoolExecutor(12)
|
||||||
|
}
|
||||||
|
sched = BlockingScheduler(executors=executors)
|
||||||
|
start_check_results_job(sched)
|
||||||
|
sched.print_jobs()
|
||||||
|
sched.start()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
config_and_start_jobs()
|
||||||
+5
-4
@@ -6,13 +6,14 @@ from request_sender_test import send_request_for_file_list
|
|||||||
|
|
||||||
|
|
||||||
def start_book_appointment():
|
def start_book_appointment():
|
||||||
file_list = ['~/Desktop/contact_list_2025-06-09_2.xlsx']
|
# file_list = ['~/Desktop/contact_list_2025-09-08.xlsx']
|
||||||
send_request_for_file_list(file_list=file_list, thread_number=30,
|
file_list = ['~/Desktop/contact_list_2025-11-28.xlsx']
|
||||||
data_queue_name=MORNING_DATA_CACHE, stop_at_hour=11, stop_at_mins=20)
|
send_request_for_file_list(file_list=file_list, thread_number=73,
|
||||||
|
data_queue_name=MORNING_DATA_CACHE, stop_at_hour=11, stop_at_mins=10)
|
||||||
|
|
||||||
|
|
||||||
def start_check_results_job(sched):
|
def start_check_results_job(sched):
|
||||||
sched.add_job(start_book_appointment, 'cron', day_of_week='mon-sat', hour='10',
|
sched.add_job(start_book_appointment, 'cron', day_of_week='mon-sun', hour='10',
|
||||||
minute='30',
|
minute='30',
|
||||||
misfire_grace_time=10,
|
misfire_grace_time=10,
|
||||||
second='10', timezone='Europe/Paris', max_instances=1, args=[])
|
second='10', timezone='Europe/Paris', max_instances=1, args=[])
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from db.mongo_manager import MONGO_DB_URL
|
||||||
|
|
||||||
|
# ================= 配置区域 =================
|
||||||
|
# 数据库连接信息
|
||||||
|
MONGO_HOST = "mongo.lpaconsulting.fr"
|
||||||
|
MONGO_PORT = "27017"
|
||||||
|
MONGO_DB_NAME = "appointment" # 你要备份/恢复的数据库名
|
||||||
|
|
||||||
|
# Get MongoDB credentials from environment variables
|
||||||
|
MONGO_USER = os.getenv(
|
||||||
|
"MONGO_USER", "appointment"
|
||||||
|
) # Default to 'appointment' if not set
|
||||||
|
MONGO_PASS = os.getenv("MONGO_PASS", "Rdv@2022") # Default to 'Rdv@2022' if not set
|
||||||
|
|
||||||
|
# 备份存放的根目录
|
||||||
|
BACKUP_DIR_ROOT = "./mongo_backups"
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
|
||||||
|
def get_auth_args():
|
||||||
|
"""构建认证参数列表"""
|
||||||
|
args = []
|
||||||
|
if MONGO_USER and MONGO_PASS:
|
||||||
|
args.extend(
|
||||||
|
[
|
||||||
|
"--username",
|
||||||
|
MONGO_USER,
|
||||||
|
"--password",
|
||||||
|
MONGO_PASS,
|
||||||
|
"--authenticationDatabase",
|
||||||
|
"appointment",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def backup_mongo():
|
||||||
|
"""执行备份操作"""
|
||||||
|
# 1. 创建带有时间戳的备份文件夹
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
backup_path = os.path.join(BACKUP_DIR_ROOT, timestamp)
|
||||||
|
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
os.makedirs(backup_path)
|
||||||
|
|
||||||
|
print(f"[*] 开始备份数据库: {MONGO_DB_NAME} 到 {backup_path} ...")
|
||||||
|
|
||||||
|
# 2. 构建 mongodump 命令
|
||||||
|
# 命令格式: mongodump --host <host> --port <port> --db <db> --out <path> [auth]
|
||||||
|
cmd = [
|
||||||
|
"mongodump",
|
||||||
|
"--host",
|
||||||
|
MONGO_HOST,
|
||||||
|
"--port",
|
||||||
|
MONGO_PORT,
|
||||||
|
"--db",
|
||||||
|
MONGO_DB_NAME,
|
||||||
|
"--out",
|
||||||
|
backup_path,
|
||||||
|
]
|
||||||
|
|
||||||
|
# 添加认证参数
|
||||||
|
cmd.extend(get_auth_args())
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 3. 执行命令
|
||||||
|
result = subprocess.run(cmd, check=True, text=True, capture_output=True)
|
||||||
|
print(f"[+] 备份成功!")
|
||||||
|
print(f" 存储路径: {backup_path}")
|
||||||
|
print(f" 日志: {result.stderr}") # mongodump 通常把进度输出到 stderr
|
||||||
|
return backup_path
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"[-] 备份失败: {e}")
|
||||||
|
print(f" 错误信息: {e.stderr}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def restore_mongo(backup_source_path):
|
||||||
|
"""
|
||||||
|
执行恢复操作
|
||||||
|
backup_source_path: 备份文件夹的路径 (例如 ./mongo_backups/2023-10-27_10-00-00)
|
||||||
|
"""
|
||||||
|
# mongodump 的输出结构通常是: backup_dir/db_name/collection.bson
|
||||||
|
# 所以我们需要指向具体的数据库文件夹,或者指向父文件夹并指定 --db
|
||||||
|
|
||||||
|
target_dir = os.path.join(backup_source_path, MONGO_DB_NAME)
|
||||||
|
|
||||||
|
if not os.path.exists(target_dir):
|
||||||
|
print(
|
||||||
|
f"[-] 错误: 在路径 {backup_source_path} 下找不到数据库 {MONGO_DB_NAME} 的备份文件。"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"[*] 开始恢复数据库: {MONGO_DB_NAME} 从 {target_dir} ...")
|
||||||
|
|
||||||
|
# 构建 mongorestore 命令
|
||||||
|
# 命令格式: mongorestore --host <host> --port <port> --db <db> <path_to_bson_files> [auth]
|
||||||
|
cmd = [
|
||||||
|
"mongorestore",
|
||||||
|
"--host",
|
||||||
|
MONGO_HOST,
|
||||||
|
"--port",
|
||||||
|
MONGO_PORT,
|
||||||
|
"--db",
|
||||||
|
MONGO_DB_NAME,
|
||||||
|
"--drop", # 警告:这会在恢复前删除现有集合,确保数据干净。根据需要移除此项。
|
||||||
|
target_dir,
|
||||||
|
]
|
||||||
|
|
||||||
|
cmd.extend(get_auth_args())
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, check=True, text=True, capture_output=True)
|
||||||
|
print(f"[+] 恢复成功!")
|
||||||
|
print(f" 日志: {result.stderr}")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"[-] 恢复失败: {e}")
|
||||||
|
print(f" 错误信息: {e.stderr}")
|
||||||
|
|
||||||
|
|
||||||
|
# ================= 主程序入口 =================
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("请选择操作:")
|
||||||
|
print("1. 备份数据库 (Backup)")
|
||||||
|
print("2. 恢复数据库 (Restore)")
|
||||||
|
|
||||||
|
choice = input("请输入数字 (1/2): ").strip()
|
||||||
|
|
||||||
|
if choice == "1":
|
||||||
|
backup_mongo()
|
||||||
|
elif choice == "2":
|
||||||
|
# 列出所有备份供用户选择
|
||||||
|
if not os.path.exists(BACKUP_DIR_ROOT):
|
||||||
|
print("[-] 没有找到备份目录。")
|
||||||
|
else:
|
||||||
|
backups = sorted(os.listdir(BACKUP_DIR_ROOT))
|
||||||
|
if not backups:
|
||||||
|
print("[-] 目录为空。")
|
||||||
|
else:
|
||||||
|
print("\n可用备份:")
|
||||||
|
for idx, name in enumerate(backups):
|
||||||
|
print(f"{idx + 1}. {name}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
idx_choice = int(input("\n请选择要恢复的备份编号: ")) - 1
|
||||||
|
if 0 <= idx_choice < len(backups):
|
||||||
|
selected_backup = os.path.join(
|
||||||
|
BACKUP_DIR_ROOT, backups[idx_choice]
|
||||||
|
)
|
||||||
|
restore_mongo(selected_backup)
|
||||||
|
else:
|
||||||
|
print("[-] 无效的选择。")
|
||||||
|
except ValueError:
|
||||||
|
print("[-] 请输入数字。")
|
||||||
|
else:
|
||||||
|
print("[-] 无效输入,退出。")
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def get_ip_geolocation(ip_address=None):
|
||||||
|
"""
|
||||||
|
使用 FreeIPAPI 查询指定 IP 地址的地理位置信息。
|
||||||
|
如果没有提供 IP 地址,将查询请求本身的公网 IP。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ip_address (str, optional): 要查询的 IP 地址。默认为 None。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含 IP 信息的字典,如果请求失败则返回 None。
|
||||||
|
"""
|
||||||
|
base_url = "https://freeipapi.com/api/json"
|
||||||
|
|
||||||
|
if ip_address:
|
||||||
|
# 如果指定了 IP,则在 URL 中添加 IP
|
||||||
|
url = f"{base_url}/{ip_address}"
|
||||||
|
else:
|
||||||
|
# 如果未指定 IP,则查询发起请求的 IP
|
||||||
|
url = base_url
|
||||||
|
|
||||||
|
print(f"正在查询 IP: {ip_address if ip_address else '当前公网 IP'}...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 发送 GET 请求,设置超时时间
|
||||||
|
response = requests.get(url, timeout=10)
|
||||||
|
|
||||||
|
# 检查 HTTP 状态码,如果不是 200 则抛出异常
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# 解析 JSON 响应
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# 检查 API 是否返回了错误信息(FreeIPAPI 在某些情况下会返回 status: 404)
|
||||||
|
if data.get('status') == 404:
|
||||||
|
print(f"查询失败:FreeIPAPI 报告未找到该 IP 地址的信息。")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"请求失败,发生网络错误: {e}")
|
||||||
|
return None
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print("响应解析失败,可能不是有效的 JSON 格式。")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def display_ip_info(data):
|
||||||
|
"""
|
||||||
|
格式化并打印 IP 地址信息。
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
print("无法获取 IP 信息。")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n--- IP 地理位置信息 ---")
|
||||||
|
|
||||||
|
# 使用 .get() 方法安全地获取数据,避免 KeyError
|
||||||
|
print(f"IP 地址: {data.get('ipAddress', 'N/A')}")
|
||||||
|
print(f"国家: {data.get('countryName', 'N/A')}")
|
||||||
|
print(f"国家代码: {data.get('countryCode', 'N/A')}")
|
||||||
|
print(f"城市: {data.get('cityName', 'N/A')}")
|
||||||
|
print(f"邮编: {data.get('zipCode', 'N/A')}")
|
||||||
|
print(f"时区: {data.get('timeZone', 'N/A')}")
|
||||||
|
# 经纬度
|
||||||
|
latitude = data.get('latitude', 'N/A')
|
||||||
|
longitude = data.get('longitude', 'N/A')
|
||||||
|
print(f"纬度/经度: {latitude} / {longitude}")
|
||||||
|
print(f"ISP/组织: {data.get('isp', 'N/A')}")
|
||||||
|
print("------------------------")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
主执行函数。可以接受命令行参数作为要查询的 IP 地址。
|
||||||
|
"""
|
||||||
|
# 检查是否有命令行参数传入
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# 取第一个参数作为要查询的 IP 地址
|
||||||
|
ip_to_query = sys.argv[1]
|
||||||
|
print(f"检测到命令行参数: {ip_to_query}")
|
||||||
|
|
||||||
|
ip_info = get_ip_geolocation(ip_to_query)
|
||||||
|
display_ip_info(ip_info)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 1. 查询当前公网 IP (不传参数)
|
||||||
|
print("\n--- 示例 1: 查询当前公网 IP ---")
|
||||||
|
my_ip_info = get_ip_geolocation()
|
||||||
|
display_ip_info(my_ip_info)
|
||||||
|
|
||||||
|
# 2. 查询特定 IP 地址 (例如:Google 的 DNS 服务器 8.8.8.8)
|
||||||
|
print("\n--- 示例 2: 查询特定 IP 地址 (8.8.8.8) ---")
|
||||||
|
google_dns_ip = "8.8.8.8"
|
||||||
|
google_ip_info = get_ip_geolocation(google_dns_ip)
|
||||||
|
display_ip_info(google_ip_info)
|
||||||
|
|
||||||
|
|
||||||
|
# 脚本入口点
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ip_info = get_ip_geolocation("80.13.246.205")
|
||||||
|
print(ip_info)
|
||||||
|
display_ip_info(ip_info)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user