Compare commits

...

74 Commits

Author SHA1 Message Date
panleicim 7020866e92 fix for migration cached data 2026-04-17 00:30:00 +02:00
panleicim a2bb4caa74 exclude proxy for inbox.lv 2026-04-16 11:17:39 +02:00
panleicim d45d6f4d7d proxy for inbox.lv 2026-04-09 08:02:23 +02:00
panleicim c84266f8fd add timeout for reading mails 2026-04-04 11:28:49 +02:00
panleicim 1fa29ebd37 add deps to requirements.txt 2026-04-02 23:32:57 +02:00
panleicim ea8673540c print failed gmx mails 2026-03-28 12:13:06 +01:00
panleicim 228c0b9bbb use proxy while reading gmx mails 2026-03-28 11:01:08 +01:00
Lei PAN b224c75ad0 password in env 2026-03-06 19:55:25 +01:00
panleicim 945388bdb5 Merge branch 'master' of gitlab.lpaconsulting.fr:panleicim/appointment_request 2025-12-27 10:16:28 +01:00
Lei PAN ae7c6c13fb Merge branch 'master' of gitlab.lpaconsulting.fr:panleicim/appointment_request 2025-12-21 23:11:11 +01:00
Lei PAN 4e174a02aa Merge branch 'master' of git.lpaconsulting.fr:panleicim/appointment_request 2025-12-21 23:10:06 +01:00
Lei PAN 0719dbdf89 migrate 2 queues to bak queue 2025-12-21 23:09:46 +01:00
panleicim c487defeed Merge branch 'master' of gitlab.lpaconsulting.fr:panleicim/appointment_request 2025-12-21 11:11:43 +01:00
panleicim 717896d92e use contact_list_2025-11-28 contact list 2025-12-21 11:10:24 +01:00
panleicim 6bc925a06b migrate moring_data_cache_2 too 2025-12-20 20:39:34 +01:00
panleicim d7e96b0b08 Merge branch 'feature/5_1_12' 2025-12-20 11:20:56 +01:00
panleicim 2073dd2377 use mongo2 db 2025-12-17 11:18:46 +01:00
panleicim 6438e4dbd3 added script to backup db 2025-12-16 09:56:04 +01:00
panleicim c7a619503b define PROXY_TIMEOUT_S 2025-12-13 10:29:23 +01:00
panleicim f591ff14ac use 5.1.12 tags.js 2025-12-12 14:50:23 +01:00
panleicim 0a7657f716 Merge branch 'refs/heads/feature/get_ip_geo_info' 2025-12-10 23:36:55 +01:00
panleicim 9085a3c1c4 use 100 threads to read mails 2025-12-09 10:30:43 +01:00
panleicim 539042881e add some comments 2025-12-06 11:08:36 +01:00
panleicim e0b1b2a4f4 correction on uploading timestamp while sending appointment request 2025-12-06 11:06:42 +01:00
Lei PAN 727cee69bb add timestamp prefix with 'ap:' while send appointment request 2025-12-05 22:48:38 +01:00
Lei PAN fcc9ef1b98 delete unused code 2025-12-05 20:52:21 +01:00
Lei PAN 38d50c77dc remove type from ReserveResultPojo 2025-12-05 20:43:30 +01:00
panleicim e954055042 can insert timestamp to mongo db 2025-12-05 17:46:57 +01:00
Lei PAN 0cf94d44fc optimization on mail_reader_all_contacts.py 2025-12-01 17:14:56 +01:00
panleicim 605bc8b252 use requests instead of curl_cffi 2025-12-01 15:52:03 +01:00
panleicim a689e66635 use FreeIPAPI.py to get ip's geo information 2025-12-01 14:34:07 +01:00
panleicim 026e06a6b3 support chrome version 142 2025-11-25 14:46:49 +01:00
panleicim c98ded5250 use iproyal proxy to validate links 2025-11-25 10:16:04 +01:00
panleicim 02562f1c50 ignore model filter 2025-11-24 09:02:30 +01:00
panleicim 4824f17881 save model information used by link validation 2025-11-20 23:33:27 +01:00
panleicim 9c06034579 save model information used by link validation 2025-11-20 23:33:22 +01:00
panleicim e204c4a6da save model information used by link validation 2025-11-20 23:33:04 +01:00
panleicim 4a33094140 use oxylab proxy 2025-11-18 23:42:44 +01:00
Lei PAN 354dbe3f15 support chrome 117 2025-11-14 23:45:08 +01:00
panleicim 93f8367b37 use ua with cookies 2025-11-14 14:13:32 +01:00
panleicim 244af8a290 use anyip proxy 2025-11-13 16:58:08 +01:00
panleicim 0f75bc03e1 handle empty model 2025-11-12 23:15:08 +01:00
panleicim 8786280209 filter via model 2025-11-12 17:03:28 +01:00
panleicim 04fbf87490 use iproyal proxies 2025-11-11 11:21:02 +01:00
panleicim cd0f0ebebc try to use 5.1.8 2025-11-11 10:19:08 +01:00
panleicim 6c704d69e3 Merge branch 'master' into feature/ip_royal_proxy 2025-11-11 10:14:09 +01:00
panleicim 468fcea934 rangement de docs 2025-11-11 10:13:46 +01:00
panleicim ec18b9bf68 use ip royal proxy to validate links 2025-11-08 14:13:41 +01:00
panleicim 2d54c90d43 use ip royal proxy to validate links 2025-11-08 14:13:27 +01:00
panleicim d8cba15006 added deps to requirements.txt 2025-11-05 16:15:40 +01:00
panleicim 5b87938acf use contact_list_file of 2025-10-30 2025-11-05 16:10:08 +01:00
panleicim a789a81fbd filter links with device model 2025-10-13 09:16:54 +02:00
panleicim 02d7b85190 do not read ms mails 2025-10-13 08:59:47 +02:00
panleicim 8936129558 try to add model to link validator 2025-10-13 08:58:38 +02:00
panleicim e6282083ba remove unused codes 2025-09-29 13:24:25 +02:00
panleicim d21f64bfc2 Merge branch 'master' of git.lpaconsulting.fr:panleicim/appointment_request 2025-09-18 15:36:17 +02:00
panleicim 474c9bf339 specify type of passport_id 2025-09-18 15:36:09 +02:00
panleicim 7eab86c910 try to scheduler multi tasks 2025-09-18 14:14:21 +02:00
panleicim 57ae14049e try to scheduler multi tasks 2025-08-19 08:42:35 +02:00
panleicim ca49028e98 more threads for link validation 2025-08-16 14:40:14 +02:00
panleicim 32eb83c5d6 added jspl_encoder_wrapper.py 2025-08-13 16:26:49 +02:00
panleicim 9b191be6d6 added jspl_encoder_wrapper.py 2025-08-13 08:58:09 +02:00
panleicim 8a6444cb14 more fields in JsDataPojo 2025-08-09 23:28:52 +02:00
panleicim 1b18804b2b check always saigecong1990@pissmail.com 2025-07-23 11:26:52 +02:00
panleicim aa540ac622 Merge branch 'feature/with_lan' of git.lpaconsulting.fr:panleicim/appointment_request into feature/with_lan 2025-07-15 19:29:56 +02:00
panleicim c9ee7d9a4f update requirements 2025-07-15 19:28:20 +02:00
panleicim f71650c347 fix mail read errors 2025-07-15 18:59:09 +02:00
panleicim a690ca7fe5 syntax fix 2025-07-15 18:32:02 +02:00
panleicim ea07867b67 add sleep to lan helper 2025-07-12 11:39:38 +02:00
panleicim 1eedb1468e specify the subject for lan mail helper 2025-07-11 19:25:18 +02:00
panleicim 0870a040b6 Merge branch 'refs/heads/feature/token_from_cookies' into feature/with_lan
# Conflicts:
#	mail/mail_reader_all_contacts.py
#	queue_message/appointmentrequestsender.py
2025-07-11 12:50:50 +02:00
panleicim 8ae6a7593b Merge branch 'feature/5_0_2' into feature/with_lan 2025-07-11 12:07:32 +02:00
panleicim 343a14f6a2 read hotmail after appointment request 2025-06-28 12:56:42 +02:00
panleicim 62cdb55da2 read hotmail after appointment request 2025-06-28 11:14:56 +02:00
107 changed files with 7132 additions and 812 deletions
View File
+445
View File
@@ -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);
+189
View File
@@ -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
View File
@@ -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()
+423
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+5 -1
View File
@@ -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()
+539
View File
@@ -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}")
+128
View File
@@ -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
View File
@@ -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
View File
@@ -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 =====")
+6 -5
View File
@@ -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)
+6 -10
View File
@@ -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,
} }
+65 -8
View File
@@ -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
View File
@@ -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
-87
View 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()
+24 -16
View File
@@ -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",
+4
View File
@@ -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
+39 -17
View File
@@ -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:
-221
View File
@@ -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")
+5 -6
View File
@@ -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)
+8
View File
@@ -0,0 +1,8 @@
APScheduler==3.10.4
curl_cffi==0.7.1
openpyxl
pika
schedule
python-dotenv
PySocks
imapclient
+26
View File
@@ -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()
+33
View File
@@ -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
View File
@@ -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=[])
+162
View File
@@ -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("[-] 无效输入,退出。")
+106
View File
@@ -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