1
0
forked from BRT/arc
arc/scripts/ozon/get_card_info.js

359 lines
17 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ozon/get_card_info.js
//
// JS-порт Ozon::GetCardInfoScript. Логика 1:1 с C++ (см.
// android/ozon/GetCardInfoScript.cpp). Самый длинный из Ozon-скриптов:
// сканирует все карты с home screen, для каждой раскрывает полный номер
// через "Показать" с retry-логикой и постит на сервер.
//
// Контракт:
// params.deviceId — серийный или android_id
// params.appCode — "ozon"
// params.lastNumbers — если задан, обрабатывается только одна карта
// Возвращает:
// "" / null / undefined → success
// строка → бизнес-ошибка
function run(host) {
var deviceId = host.params.deviceId;
var appCode = host.params.appCode;
var lastNumbers = host.params.lastNumbers || "";
var counter = 0;
var device = host.dao.deviceGetById(deviceId);
if (!device || !device.id) {
device = host.dao.deviceFindByAdbSerial(deviceId);
}
if (device && device.adbSerial) {
deviceId = device.adbSerial;
}
var app = host.dao.bankProfileGet(device.id, appCode);
if (!device || !device.id || !app || app.id < 0) {
var msg = "Device or app not found: " + deviceId + " " + appCode;
host.log.error(msg);
return msg;
}
host.dump.beginScope("ozon", "FETCH_CARDS", deviceId, {});
try {
// 1. Читаем текущий экран
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
// 2. Закрываем рекламный bottom sheet, если открыт
var adNode = host.parser.tryToFindAdBanner();
if (!adNode.isEmpty) {
host.log.debug("Ad banner detected, closing...");
host.adb.makeTap(deviceId, adNode.x, adNode.y);
host.timer.sleep(1000);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
}
// 3. Свежий дамп перед проверкой домашнего экрана
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
// 4. Если не на домашнем — перезапускаем приложение
if (!host.parser.isHomeScreen()) {
host.log.debug("Not on home screen, restarting app...");
if (!host.helpers.runAppAndGoToHomeScreen(deviceId, app.package, app.pinCode,
device.screenWidth, device.screenHeight)) {
var err = "Cannot reach home screen: " + device.name + " (" + deviceId + ")";
host.log.warn(err);
host.adb.tryToKillApplication(deviceId, app.package);
return err;
}
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
}
// 5. Закрываем баннеры
host.helpers.tryToCloseAllBannersOnHomePage(deviceId, device.screenWidth, device.screenHeight);
host.timer.sleep(500);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
// 6. Парсим имя/телефон, если в БД ещё нет
var hasProfileInfo = !!(app.fullName) && !!(app.phone);
if (!hasProfileInfo) {
var userNameNode = host.parser.findUserNameOnHomeScreen(device.screenHeight);
if (!userNameNode.isEmpty) {
host.log.debug("Navigating to profile to parse name/phone...");
host.adb.makeTap(deviceId, userNameNode.x, userNameNode.y);
host.timer.sleep(2500);
for (var a = 0; a < 5; ++a) {
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
if (host.parser.isProfileScreen()) break;
host.timer.sleep(1000);
}
if (host.parser.isProfileScreen()) {
var fullName = host.parser.parseProfileFullName();
var phone = host.parser.parseProfilePhone();
host.log.debug("Profile parsed - fullName: " + fullName + " phone: " + phone);
host.dao.bankProfileUpdateProfileInfo(app.id, fullName, phone, "");
} else {
host.log.warn("Profile screen not reached, skipping profile parse");
}
host.adb.goBack(deviceId);
host.timer.sleep(1500);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
} else {
host.log.debug("Username not found on home, skipping profile parse");
}
} else {
host.log.debug("Profile info already in DB, skipping profile parse");
}
// 7. Проверяем что снова на домашнем экране
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
if (!host.parser.isHomeScreen()) {
host.log.debug("Not on home screen before card search, restarting...");
if (!host.helpers.runAppAndGoToHomeScreen(deviceId, app.package, app.pinCode,
device.screenWidth, device.screenHeight)) {
var err2 = "Cannot reach home screen before card search: " + device.name + " (" + deviceId + ")";
host.log.warn(err2);
host.adb.tryToKillApplication(deviceId, app.package);
return err2;
}
host.helpers.tryToCloseAllBannersOnHomePage(deviceId, device.screenWidth, device.screenHeight);
host.timer.sleep(500);
}
// 8. Ждём пока карты подгрузятся
var cards = [];
for (var attempt = 0; attempt < 5; ++attempt) {
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
cards = host.parser.parseCardsOnHomeScreen();
if (cards.length > 0) break;
host.log.debug("No cards found on home screen, attempt " + (attempt + 1) + "/5");
host.timer.sleep(1500);
}
if (cards.length === 0) {
var noCardsErr = "No cards found on home screen: " + deviceId;
host.log.warn(noCardsErr);
host.adb.tryToKillApplication(deviceId, app.package);
return noCardsErr;
}
host.log.debug("Found " + cards.length + " card(s) on home screen");
// 9. Апсёртим аккаунты
for (var ci = 0; ci < cards.length; ++ci) {
cards[ci].deviceId = device.id;
cards[ci].appCode = appCode;
cards[ci].status = "active";
if (!host.dao.materialUpsert(cards[ci])) {
host.log.warn("Failed to upsert account for card " + cards[ci].lastNumbers);
} else {
host.log.debug("Upserted account: " + cards[ci].lastNumbers
+ " " + cards[ci].description + " " + cards[ci].amount);
}
}
// 10. Баланс
var totalAmount = cards[0].amount;
if (totalAmount > 0.0) {
host.dao.bankProfileUpdateAmount(app.id, totalAmount);
}
// 11. createDevice если нет apiId
host.helpers.createDeviceIfNeeded(device);
// 12. Регистрируем/обновляем bank profile на сервере
host.helpers.createBankProfileIfNeeded(deviceId, appCode, totalAmount);
// 13. Для каждой карты раскрываем полный номер через "Показать"
for (var i = 0; i < cards.length; ++i) {
var card = cards[i];
if (lastNumbers && card.lastNumbers !== lastNumbers) continue;
host.log.debug("Getting card number for " + card.lastNumbers);
var existing = host.dao.materialFindAppAccount(device.id, appCode, card.lastNumbers);
if (existing && existing.id >= 0 && existing.cardNumber) {
host.log.debug("Card " + card.lastNumbers + " already has number, skipping");
continue;
}
var cardBtn = host.parser.findCardButtonByLastNumbers(card.lastNumbers);
if (cardBtn.isEmpty) {
host.log.warn("Card button not found for " + card.lastNumbers);
continue;
}
host.adb.makeTap(deviceId, cardBtn.x, cardBtn.y);
// 13a. Скроллим WebView вверх, пока "Показать" не окажется в верхней трети экрана
var targetTopY = Math.floor(device.screenHeight / 3);
for (var s = 0; s < 6; ++s) {
host.timer.sleep(s === 0 ? 2000 : 600);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
var cand = host.parser.findButtonNode("Показать", false);
if (cand.isEmpty) {
if (host.parser.isHomeScreen()) {
var rb = host.parser.findCardButtonByLastNumbers(card.lastNumbers);
if (!rb.isEmpty) {
host.log.debug("Still on home before scroll, re-tapping card " + card.lastNumbers);
host.adb.makeTap(deviceId, rb.x, rb.y);
}
}
continue;
}
if (cand.y <= targetTopY) {
host.log.debug("'Показать' already at top y=" + cand.y);
break;
}
var swipeFromY = cand.y;
var swipeToY = Math.floor(device.screenHeight / 6);
host.log.debug("Scrolling 'Показать' up: " + swipeFromY + " -> " + swipeToY);
host.adb.makeSwipe(deviceId, Math.floor(device.screenWidth / 2), swipeFromY,
Math.floor(device.screenWidth / 2), swipeToY);
}
// 13b. Ждём стабильной геометрии: "Показать" в одной строке с "Реквизиты карты"
// И координаты не меняются в двух подряд дампах.
var showBtn = null;
var prevShowY = -1;
for (var w = 0; w < 10; ++w) {
host.timer.sleep(w === 0 ? 2000 : 500);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
var c = host.parser.findButtonNode("Показать", false);
var req = host.parser.findTextNode("Реквизиты карты");
if (c.isEmpty || req.isEmpty) {
if (host.parser.isHomeScreen()) {
var rb2 = host.parser.findCardButtonByLastNumbers(card.lastNumbers);
if (!rb2.isEmpty) {
host.log.debug("Still on home screen, re-tapping card " + card.lastNumbers);
host.adb.makeTap(deviceId, rb2.x, rb2.y);
}
}
prevShowY = -1;
host.log.debug("Requisites not ready, attempt " + (w + 1) + "/10");
continue;
}
if (Math.abs(c.y1 - req.y1) > 10) {
prevShowY = -1;
host.log.debug("'Показать' not aligned with 'Реквизиты карты', waiting reflow");
continue;
}
if (prevShowY >= 0 && Math.abs(c.y - prevShowY) <= 5) {
showBtn = c;
break;
}
prevShowY = c.y;
host.log.debug("'Показать' seen at y=" + c.y + ", waiting for stable frame");
}
if (!showBtn) {
host.log.warn("'Показать' not stable for card " + card.lastNumbers);
host.adb.goBack(deviceId);
host.timer.sleep(1500);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
continue;
}
host.log.debug("'Показать' at " + showBtn.x + " " + showBtn.y);
host.adb.makeTap(deviceId, showBtn.x, showBtn.y);
// 13c. Ждём появления полного номера карты
var cardNumber = "";
for (var t = 0; t < 10; ++t) {
host.timer.sleep(2000);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
cardNumber = host.parser.findFullCardNumber();
if (cardNumber) {
host.log.debug("Card number found on attempt " + (t + 1));
break;
}
// Баннер "Понятно" — закрываем и повторно тапаем "Показать"
var banner = host.parser.findTextNode("Понятно");
if (!banner.isEmpty) {
host.log.debug("Banner 'Понятно' detected, closing...");
host.adb.makeTap(deviceId, banner.x, banner.y);
host.timer.sleep(1500);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
var retry = host.parser.findButtonNode("Показать", false);
if (!retry.isEmpty) {
host.log.debug("Re-tapping 'Показать' after banner close");
host.adb.makeTap(deviceId, retry.x, retry.y);
}
continue;
}
// "Подождите" — загрузка идёт
if (!host.parser.findButtonNode("Подождите", false).isEmpty) {
host.log.debug("'Подождите' visible, waiting... attempt " + (t + 1) + "/10");
continue;
}
// "Скрыть" видна — номер должен быть, но парсер не нашёл
if (!host.parser.findButtonNode("Скрыть", false).isEmpty) {
host.log.debug("'Скрыть' visible but card number not parsed, attempt " + (t + 1) + "/10");
continue;
}
// Повторный тап "Показать"
var retry2 = host.parser.findButtonNode("Показать", false);
if (!retry2.isEmpty) {
host.log.debug("Re-tapping 'Показать', attempt " + (t + 1) + "/10");
host.adb.makeTap(deviceId, retry2.x, retry2.y);
} else {
host.log.debug("Card number not visible, attempt " + (t + 1) + "/10");
}
}
if (cardNumber) {
host.log.debug("Card " + card.lastNumbers + " full number: " + cardNumber);
var saved = host.dao.materialFindAppAccount(device.id, appCode, card.lastNumbers);
if (saved && saved.id >= 0) {
host.dao.materialUpdateCardNumber(saved.id, cardNumber);
}
} else {
var noNumErr = "Card number not found for " + card.lastNumbers;
host.log.warn(noNumErr);
var s2 = host.dao.materialFindAppAccount(device.id, appCode, card.lastNumbers);
if (s2 && s2.id >= 0) {
host.dao.materialUpdateAccountById(s2.id, "off", s2.description, s2.currency);
}
}
// Возвращаемся на домашний для следующей карты
if (i < cards.length - 1) {
host.adb.goBack(deviceId);
host.timer.sleep(2000);
host.parser.parseAndSaveXml(host.adb.getScreenDumpAsXml(deviceId, ++counter));
}
}
// 14. Отправляем материалы на сервер — после того как все номера в БД
var updatedApp = host.dao.bankProfileGet(device.id, appCode);
if (updatedApp && updatedApp.bankProfileId) {
for (var k = 0; k < cards.length; ++k) {
var dbCard = host.dao.materialFindAppAccount(device.id, appCode, cards[k].lastNumbers);
if (!dbCard || dbCard.id < 0) continue;
if (!dbCard.cardNumber) continue;
if (dbCard.materialId) {
host.log.debug("material already exists for card " + dbCard.lastNumbers + ", skipping");
continue;
}
host.helpers.postMaterial(dbCard, updatedApp.bankProfileId, updatedApp.fullName);
host.log.debug("postMaterial done for card " + dbCard.lastNumbers);
}
} else {
host.log.warn("bankProfileId empty, skipping materials");
}
host.adb.tryToKillApplication(deviceId, app.package);
host.dump.endScope("");
return ""; // success
} catch (e) {
host.dump.endScope(String(e));
throw e;
}
}