Skip to content

Instantly share code, notes, and snippets.

@vslinko
Last active February 6, 2018 01:28
Show Gist options
  • Save vslinko/577a9aaf7563055ed1dcbb6e2cc84e72 to your computer and use it in GitHub Desktop.
Save vslinko/577a9aaf7563055ed1dcbb6e2cc84e72 to your computer and use it in GitHub Desktop.
const puppeteer = require('puppeteer');
const readline = require('readline-sync');
const util = require('util');
const fs = require('fs');
const USERNAME = 'USERNAME';
const EXPECTED_TEMPLATE_NAME = 'EXPECTED_TEMPLATE_NAME';
const EXPECTED_ACCOUNT_NUMBER = 'EXPECTED_ACCOUNT_NUMBER';
function questionAsync(text, hidden = false) {
return new Promise((resolve) => {
resolve(
readline.question(text, {
hideEchoBack: hidden,
})
);
});
}
function priceToNumber(price) {
if (!/^[\d\s]+([\.,]\d+)?$/.test(price)) {
throw new Error(`Невалидная сумма "${price}"`);
}
return Number(price.replace(',', '.').replace(/[^\.0-9]/g, ''));
}
(async () => {
let browser, page;
const folderName = new Date().getTime();
try {
fs.mkdirSync(String(folderName));
console.log('Открываю браузер');
browser = await puppeteer.launch();
console.log('Открываю главную');
page = await browser.newPage();
await page.goto('https://ib.psbank.ru');
console.log('Открываю попап авторизации');
await page.waitForSelector('.login-link');
await page.click('.login-link');
await page.waitForSelector('input[name=clientNumber]');
console.log('Авторизуюсь');
await page.keyboard.type(USERNAME);
const password = await questionAsync('Введите пароль: ', true);
await page.focus('input[name=password]');
await page.keyboard.type(password);
await page.screenshot({path: `${folderName}/1-loginPage.png`});
await page.click('input[value="Войти"]');
await Promise.all([
page.waitForSelector('.code-input-number'),
page.waitForSelector('input[name=secondStepSmsCode]'),
]);
const firstSmsId = (await (await (await page.$('.code-input-number')).getProperty('textContent')).jsonValue()).replace(/ /g, '');
const firstSmsText = await questionAsync(`Введите код ${firstSmsId}: `);
await page.focus('input[name=secondStepSmsCode]');
await page.keyboard.type(firstSmsText);
console.log('Жду загрузки дашборда');
await Promise.all([
page.waitForSelector('.my-templates h2 a'),
page.waitForSelector('.balance'),
]);
await page.screenshot({path: `${folderName}/2-mainPage.png`});
const balance = await page.evaluate(() => {
return document.querySelector('.balance').textContent;
});
const balanceNumber = priceToNumber(balance.trim());
const toPay = balanceNumber > 10000 ? 10000 : balanceNumber;
console.log('Текущий баланс:', balance);
console.log('Сумма операции:', toPay);
console.log('Открываю страницу шаблонов');
await page.click('.my-templates h2 a');
await page.waitForSelector('span.name[role=button]');
await page.screenshot({path: `${folderName}/3-templatesPage.png`});
console.log('Открываю нужный шаблон');
const templates = await page.$$('span.name[role=button]');
const templateNames = await Promise.all(templates.map(async (template) => {
const property = await template.getProperty('textContent');
return await property.jsonValue();
}));
const templateIndex = templateNames.indexOf(EXPECTED_TEMPLATE_NAME);
if (templateIndex < 0) {
throw new Error(`Не могу найти шаблон "${EXPECTED_TEMPLATE_NAME}"`);
}
await templates[templateIndex].click();
console.log('Проверяю данные транзакции');
await Promise.all([
page.waitForSelector('input[name*=Sum]'),
page.waitForSelector('input[name*=ReceiverAccountNumber]'),
page.waitForSelector('.op-footer .button-primary'),
]);
await page.focus('input[name*=Sum]');
await page.keyboard.type(String(toPay));
await page.focus('input[name*=ReceiverAccountNumber]');
const {accountNumber, operationAmount} = await page.evaluate(() => {
return {
operationAmount: document.querySelector('input[name*=Sum]').value,
accountNumber: document.querySelector('input[name*=ReceiverAccountNumber]').value,
};
});
if (accountNumber !== EXPECTED_ACCOUNT_NUMBER) {
throw new Error(`Неверный номер счета "${accountNumber}"`);
}
if (priceToNumber(operationAmount) !== toPay) {
throw new Error(`Неверная сумма операции "${operationAmount}"`);
}
console.log('Подтверждаю транзакцию');
const nextButton = await page.$('.op-footer .button-primary');
const nextButtonText = await (await nextButton.getProperty('textContent')).jsonValue();
if (nextButtonText !== 'Далее') {
throw new Error(`Неверный текст кнопки "${nextButtonText}"`);
}
await page.screenshot({path: `${folderName}/4-popup.png`});
await nextButton.click();
await Promise.all([
page.waitForSelector('.code-number'),
page.waitForSelector('input[name=secondStepSmsCode]'),
]);
const comissionAlert = await page.evaluate(() => {
const popups = document.querySelectorAll('.operation-execution');
if (popups.length !== 1) {
return true;
}
return popups[0].textContent.indexOf('Комиссия') >= 0;
});
if (comissionAlert) {
throw new Error(`Обнаружена комиссия`);
}
await page.screenshot({path: `${folderName}/5-popup_sms.png`});
const secondSmsId = await (await (await page.$('.code-number')).getProperty('textContent')).jsonValue();
const secondSmsText = await questionAsync(`Введите код ${secondSmsId}: `);
await page.focus('input[name=secondStepSmsCode]');
await page.keyboard.type(secondSmsText);
console.log('Жду завершения операции');
await page.waitForFunction(() => {
return document.querySelector('.step-caption').textContent === "Операция проведена!";
});
await page.screenshot({path: `${folderName}/6-popup_result.png`});
console.log('Операция проведена!');
await browser.close();
} catch (e) {
console.error(e);
if (page) {
await page.screenshot({path: `${folderName}/error.png`});
}
if (browser) {
await browser.close();
}
}
})();
{
"name": "psb",
"version": "1.0.0",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"puppeteer": "^1.0.0",
"readline-sync": "^1.4.7"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment