diff --git a/data_providers/avtocod.js b/data_providers/avtocod.js index f0e23e5..e580130 100644 --- a/data_providers/avtocod.js +++ b/data_providers/avtocod.js @@ -3,11 +3,24 @@ import Vehicle from '../models/vehicle.js'; import DebugInfo from '../models/DebugInfo.js'; import { Centrifuge } from 'centrifuge'; import WebSocket from 'ws'; +import { v4 as uuidv4 } from 'uuid'; +import crypto from 'crypto'; -const baseUrl = 'https://avtocod.ru/api/v3'; +const baseUrl = 'https://avtocod.ru/api/v4'; const tokenRefreshUrl = 'https://avtocod.ru/api/centrifuge/refresh'; +const minimalVersionUrl = 'https://avtocod.ru/api/v4/info/minimal_version?platform=ios'; // Just for getting cookies +const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0'; -let deviceToken = createHash('sha256').update(Date.now().toString()).digest().toString('hex'); +const sign = { + version: '4.6.2', + sign: '3343fc07110c5df19b840d688bc45b78', + platform: 'Win32', + browsers: ['chrome'], + score: 0.6, +}; + +let deviceToken = uuidv4().replaceAll('-', '').toUpperCase(); +var cookie = null; const myWs = function (options) { return class wsClass extends WebSocket { @@ -17,18 +30,94 @@ const myWs = function (options) { }; }; +function findValue(key, str) { + + let result = str.match(new RegExp(`"(${key}=.*?)"`)); + if(result == null) { + throw Error(`Cannot find ${key} data`); + } + return result[1]; +} + +function getSpsnCookie(html, sign) { + + let signStr = JSON.stringify(sign); + let signHex = Buffer.from(signStr).toString('hex'); + + let spsnStart = findValue('spsn', html); + return spsnStart + signHex + ';'; +} + +function getSpidCookie(html) { + + return findValue('spid', html) + ';'; +} + +function getSpscCookie(html) { + + let keyResult = html.match(/-----BEGIN.*?KEY-----.*?-----END.*?KEY-----/s); + if(keyResult == null) { + throw Error('Cannot find key for decrypting spsc data'); + } + let key = keyResult[0]; + + let srcResult = html.match(/decrypt\("(.*?)"/); + if(srcResult == null) { + throw Error('Cannot find spsc data'); + } + let dataToDecrypt = Buffer.from(srcResult[1], 'hex'); + + let decrypted = crypto.privateDecrypt({ + key, + padding: crypto.constants.RSA_PKCS1_PADDING + }, dataToDecrypt); + + return 'spsc=' + decrypted.toString() + ';'; +} + function fromBase64(data) { return Buffer.from(data, 'base64').toString('binary'); } async function getPage(url) { - let result = await fetch(url); + let result = await fetch(url, { + headers: { + 'User-Agent': userAgent, + 'cookie': await getCookies() + } + }); return await result.text(); } +async function getCookies() { + if(cookie != null) { + return cookie; + } + + let result = await fetch(minimalVersionUrl, { + headers: { + 'User-Agent': userAgent + } + }); + + let html = await result.text(); + + let spsnCookie = getSpsnCookie(html, sign); + let spidCookie = getSpidCookie(html); + let spscCookie = getSpscCookie(html); + cookie = [spsnCookie, spidCookie, spscCookie].join(' '); + return cookie; +} + async function getJson(url) { - let result = await fetch(url); - return await result.json(); + + let jsonResult = await fetch(url, { + headers: { + 'User-Agent': userAgent, + 'cookie': await getCookies() + } + }); + return await jsonResult.json(); } function decryptReport(report, hash) { @@ -76,8 +165,8 @@ function waitForReport(centrifugoConfig, channel) { let centrifuge = new Centrifuge([{ transport: 'websocket', endpoint: centrifugoConfig.uri - }], { - websocket: myWs({ headers: { + }], { + websocket: myWs({ headers: { 'Sec-Fetch-Site': 'same-site', 'Sec-Fetch-Mode': 'websocket', 'Sec-Fetch-Dest': 'websocket', @@ -117,7 +206,7 @@ function waitForReport(centrifugoConfig, channel) { class AvtocodProvider { static async getReport(number) { try { - let url = `${baseUrl}/auto/generate?number=${encodeURIComponent(number)}&device_token=${deviceToken}`; + let url = `${baseUrl}/auto/generate?number=${encodeURIComponent(number)}&device_token=${deviceToken}&type=GRZ`; let resp = await getJson(url); let html = await getPage(resp.report_uri); @@ -127,7 +216,7 @@ class AvtocodProvider { throw Error('Error getting api version hash'); } let hash = result[1]; - + result = html.match(/value:(.*report_container.*)/); if(result == null) { throw Error('Error getting report data');