UNPKG

@accounter/modern-poalim-scraper

Version:
293 lines • 14.6 kB
import inquirer from 'inquirer'; import { fetchGetWithinPage, fetchPoalimXSRFWithinPage } from '../utils/fetch.js'; import { HapoalimAccountDataSchema, } from '../zod-schemas/hapoalim-account-data-schema.js'; import { HapoalimDepositsSchema, } from '../zod-schemas/hapoalim-deposits-schema.js'; import { HapoalimForeignDepositsSchema, } from '../zod-schemas/hapoalim-foreign-deposits-schema.js'; import { HapoalimForeignTransactionsBusinessSchema, } from '../zod-schemas/hapoalim-foreign-transactions-business-schema.js'; import { HapoalimForeignTransactionsPersonalSchema, } from '../zod-schemas/hapoalim-foreign-transactions-personal-schema.js'; import { HapoalimILSTransactionsSchema, } from '../zod-schemas/hapoalim-ils-checking-transactions-schema.js'; import { SwiftTransactionSchema, } from '../zod-schemas/swift-transaction-schema.js'; import { SwiftTransactionsSchema, } from '../zod-schemas/swift-transactions-schema.js'; async function businessLogin(credentials, page, otpCallback) { const BASE_URL = 'https://biz2.bankhapoalim.co.il/ng-portals/auth/he/biz-login/authenticate'; await page.goto(BASE_URL); await page.waitForSelector('.submit-btn'); await page.type('#user-code', credentials.userCode); await page.type('#password', credentials.password); page.click('.submit-btn'); let otp; if (otpCallback) { otp = await otpCallback(); } else { const answers = await inquirer.prompt({ type: 'input', name: 'SMSPassword', message: 'Enter the code you got in SMS:', }); otp = answers.SMSPassword; } await page.type('input[formcontrolname="code"]', otp); await Promise.all([ page.waitForNavigation(), page.keyboard.press('Enter'), page.click('button.next'), ]); } async function personalLogin(credentials, page) { const BASE_URL = 'https://login.bankhapoalim.co.il/ng-portals/auth/he/'; await page.goto(BASE_URL); const userCode = credentials.userCode; const password = credentials.password; await page.waitForSelector('.login-btn'); await page.type('#userCode', userCode); await page.type('#password', password); await page.click('.login-btn'); await page.waitForNavigation(); return 0; } async function replacePassword(previousCredentials, page, otpCallback) { await page.waitForSelector('#buttonAction'); let newPassword; if (otpCallback) { newPassword = await otpCallback(); } else { const answers = await inquirer.prompt({ type: 'input', name: 'newPassword', message: 'Enter your new wanted password:', }); newPassword = answers.newPassword; } await page.type('[name="oldpassword"]', previousCredentials.password); await page.type('[name="newpassword"]', newPassword); await page.type('[name="newpassword2"]', newPassword); await Promise.all([page.waitForNavigation(), page.keyboard.press('Enter')]); await page.waitForSelector('#linkToHomePage'); await Promise.all([ page.waitForNavigation(), page.keyboard.press('Enter'), page.click('#linkToHomePage'), ]); return 0; } export async function hapoalim(page, credentials, options) { if (options?.isBusiness) { await businessLogin(credentials, page, options.otpCallback); } else { await personalLogin(credentials, page); } const result = await page.evaluate(() => { if (window?.bnhpApp?.restContext) { return window.bnhpApp.restContext; } return 'nothing'; }); // Example replace password url: // https://biz2.bankhapoalim.co.il/ABOUTTOEXPIRE/START?flow=ABOUTTOEXPIRE&state=START&expiredDate=11122020 if (result === 'nothing') { if (page.url().search('ABOUTTOEXPIRE') === -1) { return 'Unknown Error'; } await replacePassword(credentials, page, options?.otpCallback); } const apiSiteUrl = `https://${options?.isBusiness ? 'biz2' : 'login'}.bankhapoalim.co.il/${result.slice(1)}`; const now = new Date(); const startMonth = options?.duration ?? 12; const startDate = new Date(now.getFullYear(), now.getMonth() - startMonth, now.getDate() + 1); const startDateString = startDate.toISOString().substr(0, 10).replace(/-/g, ''); const endDateString = new Date().toISOString().substr(0, 10).replace(/-/g, ''); // TODO: https://www.npmjs.com/package/node-fetch-cookies return { getAccountsData: async () => { const accountDataUrl = `${apiSiteUrl}/general/accounts`; const getAccountsFunction = fetchGetWithinPage(page, accountDataUrl); if (options?.validateSchema) { const data = await getAccountsFunction; const validation = HapoalimAccountDataSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.success ? null : validation.error.issues, }; } return { data: await getAccountsFunction, isValid: null }; }, getILSTransactions: async (account) => { const fullAccountNumber = `${account.bankNumber}-${account.branchNumber}-${account.accountNumber}`; const ILSCheckingTransactionsUrl = `${apiSiteUrl}/current-account/transactions?accountId=${fullAccountNumber}&numItemsPerPage=200&retrievalEndDate=${endDateString}&retrievalStartDate=${startDateString}&sortCode=1`; const getIlsTransactionsFunction = fetchPoalimXSRFWithinPage(page, ILSCheckingTransactionsUrl, '/current-account/transactions'); if (options?.validateSchema || options?.getTransactionsDetails) { const data = await getIlsTransactionsFunction; if (options?.getTransactionsDetails && data != null) { for (const transaction of data?.transactions ?? []) { if (transaction.pfmDetails) { /* let a = */ await fetchPoalimXSRFWithinPage(page, ILSCheckingTransactionsUrl, transaction.pfmDetails); // TODO: create schema and make this attribute string / object for inputing data } if (transaction.details) { /*let b = */ await fetchPoalimXSRFWithinPage(page, ILSCheckingTransactionsUrl, transaction.details); // TODO: create schema and make this attribute string / object for inputing data } } if (!options?.validateSchema) { return { data, isValid: null }; } } if (!data) { console.log(`No ILS data found for account ${fullAccountNumber}`); return { data, isValid: true, }; } const validation = HapoalimILSTransactionsSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.success ? null : validation.error.issues, }; } return { data: await getIlsTransactionsFunction, isValid: null }; }, getForeignTransactions: async (account, isBusiness = true) => { /** * The URL includes optional "type" query param. * Setting it to "business" changes the response type. * Since accounter uses the business-specific fields, * the non-business option is commented out * */ const fullAccountNumber = `${account.bankNumber}-${account.branchNumber}-${account.accountNumber}`; const foreignTransactionsUrl = `${apiSiteUrl}/foreign-currency/transactions?accountId=${fullAccountNumber}${isBusiness ? '&type=business' : ''}&view=details&retrievalEndDate=${endDateString}&retrievalStartDate=${startDateString}&currencyCodeList=19,27,51,100,140,248&detailedAccountTypeCodeList=142&lang=he`; const getForeignTransactionsFunction = fetchGetWithinPage(page, foreignTransactionsUrl); if (options?.validateSchema) { const data = await getForeignTransactionsFunction; if (data && data['messageCode'] === 0 && data['severity'] === 'E') { return { data, errors: 'Data seems unreachable. Is the account active?', isValid: false, }; } if (isBusiness) { const validation = HapoalimForeignTransactionsBusinessSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.success ? null : validation.error.issues, }; } const validation = HapoalimForeignTransactionsPersonalSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.success ? null : validation.error.issues, }; } return { data: await getForeignTransactionsFunction, isValid: null }; }, getForeignSwiftTransactions: async (account) => { const fullAccountNumber = `${account.bankNumber}-${account.branchNumber}-${account.accountNumber}`; const foreignSwiftTransactionsUrl = `${apiSiteUrl}/foreign-trade/swiftTransactions?accountId=${fullAccountNumber}&endDate=${endDateString}&startDate=20190501`; const getForeignSwiftTransactionsFunction = fetchGetWithinPage(page, foreignSwiftTransactionsUrl); if (options?.validateSchema) { const data = await getForeignSwiftTransactionsFunction; if (data && data['messageCode'] === 0 && data['severity'] === 'E') { return { data, errors: 'Data seems unreachable. Is the account active?', isValid: false, }; } const validation = SwiftTransactionsSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.error, }; } return { data: await getForeignSwiftTransactionsFunction, isValid: null }; }, getForeignSwiftTransaction: async (account, transferCatenatedId, dataOriginCode) => { const fullAccountNumber = `${account.bankNumber}-${account.branchNumber}-${account.accountNumber}`; const foreignSwiftTransactionUrl = `${apiSiteUrl}/foreign-trade/swiftTransactions/${transferCatenatedId}?accountId=${fullAccountNumber}&dataOriginCode=${dataOriginCode}&lang=he`; const getForeignSwiftTransactionFunction = fetchGetWithinPage(page, foreignSwiftTransactionUrl); if (options?.validateSchema) { const data = await getForeignSwiftTransactionFunction; if (data && data['messageCode'] === 0 && data['severity'] === 'E') { return { data, errors: 'Data seems unreachable. Is the account active?', isValid: false, }; } const validation = SwiftTransactionSchema.safeParse(data); return { data, isValid: validation.success, errors: validation.error, }; } return { data: await getForeignSwiftTransactionFunction, isValid: null }; }, getDeposits: async (account) => { const fullAccountNumber = `${account.bankNumber}-${account.branchNumber}-${account.accountNumber}`; const depositsUrl = `${apiSiteUrl}/deposits-and-savings/deposits?accountId=${fullAccountNumber}&view=details&lang=he`; const getDepositsFunction = fetchGetWithinPage(page, depositsUrl); if (options?.validateSchema) { const data = await getDepositsFunction; if (!data) { console.log(`No deposits data found for account ${fullAccountNumber}`); return { data, isValid: true, }; } const validation = HapoalimDepositsSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.success ? null : validation.error.issues, }; } return { data: await getDepositsFunction, isValid: null }; }, getForeignDeposits: async (account) => { const fullAccountNumber = `${account.bankNumber}-${account.branchNumber}-${account.accountNumber}`; const depositsUrl = `${apiSiteUrl}/foreign-currency/revaluedDeposit?accountId=${fullAccountNumber}&lang=he`; const getDepositsFunction = fetchGetWithinPage(page, depositsUrl); if (options?.validateSchema) { const data = await getDepositsFunction; const validation = HapoalimForeignDepositsSchema.safeParse(data); return { data: validation.data ?? null, isValid: validation.success, errors: validation.success ? null : validation.error.issues, }; } return { data: await getDepositsFunction, isValid: null }; }, }; } export class HapoalimOptions { validateSchema = false; isBusiness = true; duration = 12; getTransactionsDetails = false; otpCallback = undefined; } class HapoalimPersonalCredentials { userCode = ''; password = ''; } class HapoalimBusinessCredentials extends HapoalimPersonalCredentials { } //# sourceMappingURL=hapoalim.js.map