israeli-bank-scrapers-core
Version:
Provide scrapers for all major Israeli banks and credit card companies
243 lines (241 loc) • 36.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _moment = _interopRequireDefault(require("moment/moment"));
var _debug = require("../helpers/debug");
var _fetch = require("../helpers/fetch");
var _transactions = require("../helpers/transactions");
var _transactions2 = require("../transactions");
var _baseScraper = require("./base-scraper");
var _errors = require("./errors");
var _oneZeroQueries = require("./one-zero-queries");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const HEBREW_WORDS_REGEX = /[\u0590-\u05FF][\u0590-\u05FF"'\-_ /\\]*[\u0590-\u05FF]/g;
const debug = (0, _debug.getDebug)('one-zero');
const IDENTITY_SERVER_URL = 'https://identity.tfd-bank.com/v1/';
const GRAPHQL_API_URL = 'https://mobile.tfd-bank.com/mobile-graph/graphql';
class OneZeroScraper extends _baseScraper.BaseScraper {
async triggerTwoFactorAuth(phoneNumber) {
if (!phoneNumber.startsWith('+')) {
return (0, _errors.createGenericError)('A full international phone number starting with + and a three digit country code is required');
}
debug('Fetching device token');
const deviceTokenResponse = await (0, _fetch.fetchPost)(`${IDENTITY_SERVER_URL}/devices/token`, {
extClientId: 'mobile',
os: 'Android'
});
const {
resultData: {
deviceToken
}
} = deviceTokenResponse;
debug(`Sending OTP to phone number ${phoneNumber}`);
const otpPrepareResponse = await (0, _fetch.fetchPost)(`${IDENTITY_SERVER_URL}/otp/prepare`, {
factorValue: phoneNumber,
deviceToken,
otpChannel: 'SMS_OTP'
});
const {
resultData: {
otpContext
}
} = otpPrepareResponse;
this.otpContext = otpContext;
return {
success: true
};
}
async getLongTermTwoFactorToken(otpCode) {
if (!this.otpContext) {
return (0, _errors.createGenericError)('triggerOtp was not called before calling getPermenantOtpToken()');
}
debug('Requesting OTP token');
const otpVerifyResponse = await (0, _fetch.fetchPost)(`${IDENTITY_SERVER_URL}/otp/verify`, {
otpContext: this.otpContext,
otpCode
});
const {
resultData: {
otpToken
}
} = otpVerifyResponse;
return {
success: true,
longTermTwoFactorAuthToken: otpToken
};
}
async resolveOtpToken(credentials) {
if ('otpLongTermToken' in credentials) {
if (!credentials.otpLongTermToken) {
return (0, _errors.createGenericError)('Invalid otpLongTermToken');
}
return {
success: true,
longTermTwoFactorAuthToken: credentials.otpLongTermToken
};
}
if (!credentials.otpCodeRetriever) {
return {
success: false,
errorType: _errors.ScraperErrorTypes.TwoFactorRetrieverMissing,
errorMessage: 'otpCodeRetriever is required when otpPermanentToken is not provided'
};
}
if (!credentials.phoneNumber) {
return (0, _errors.createGenericError)('phoneNumber is required when providing a otpCodeRetriever callback');
}
debug('Triggering user supplied otpCodeRetriever callback');
const triggerResult = await this.triggerTwoFactorAuth(credentials.phoneNumber);
if (!triggerResult.success) {
return triggerResult;
}
const otpCode = await credentials.otpCodeRetriever();
const otpTokenResult = await this.getLongTermTwoFactorToken(otpCode);
if (!otpTokenResult.success) {
return otpTokenResult;
}
return {
success: true,
longTermTwoFactorAuthToken: otpTokenResult.longTermTwoFactorAuthToken
};
}
async login(credentials) {
const otpTokenResult = await this.resolveOtpToken(credentials);
if (!otpTokenResult.success) {
return otpTokenResult;
}
debug('Requesting id token');
const getIdTokenResponse = await (0, _fetch.fetchPost)(`${IDENTITY_SERVER_URL}/getIdToken`, {
otpSmsToken: otpTokenResult.longTermTwoFactorAuthToken,
email: credentials.email,
pass: credentials.password,
pinCode: ''
});
const {
resultData: {
idToken
}
} = getIdTokenResponse;
debug('Requesting session token');
const getSessionTokenResponse = await (0, _fetch.fetchPost)(`${IDENTITY_SERVER_URL}/sessions/token`, {
idToken,
pass: credentials.password
});
const {
resultData: {
accessToken
}
} = getSessionTokenResponse;
this.accessToken = accessToken;
return {
success: true,
persistentOtpToken: otpTokenResult.longTermTwoFactorAuthToken
};
}
async fetchPortfolioMovements(portfolio, startDate) {
// TODO: Find out if we need the other accounts, there seems to always be one
const account = portfolio.accounts[0];
let cursor = null;
const movements = [];
while (!movements.length || new Date(movements[0].movementTimestamp) >= startDate) {
debug(`Fetching transactions for account ${portfolio.portfolioNum}...`);
const {
movements: {
movements: newMovements,
pagination
}
} = await (0, _fetch.fetchGraphql)(GRAPHQL_API_URL, _oneZeroQueries.GET_MOVEMENTS, {
portfolioId: portfolio.portfolioId,
accountId: account.accountId,
language: 'HEBREW',
pagination: {
cursor,
limit: 50
}
}, {
authorization: `Bearer ${this.accessToken}`
});
movements.unshift(...newMovements);
cursor = pagination.cursor;
if (!pagination.hasMore) {
break;
}
}
movements.sort((x, y) => new Date(x.movementTimestamp).valueOf() - new Date(y.movementTimestamp).valueOf());
const matchingMovements = movements.filter(movement => new Date(movement.movementTimestamp) >= startDate);
return {
accountNumber: portfolio.portfolioNum,
balance: !movements.length ? 0 : parseFloat(movements[movements.length - 1].runningBalance),
txns: matchingMovements.map(movement => {
const hasInstallments = movement.transaction?.enrichment?.recurrences?.some(x => x.isRecurrent);
const modifier = movement.creditDebit === 'DEBIT' ? -1 : 1;
const result = {
identifier: movement.movementId,
date: movement.valueDate,
chargedAmount: +movement.movementAmount * modifier,
chargedCurrency: movement.movementCurrency,
originalAmount: +movement.movementAmount * modifier,
originalCurrency: movement.movementCurrency,
description: this.sanitizeHebrew(movement.description),
processedDate: movement.movementTimestamp,
status: _transactions2.TransactionStatuses.Completed,
type: hasInstallments ? _transactions2.TransactionTypes.Installments : _transactions2.TransactionTypes.Normal
};
if (this.options?.includeRawTransaction) {
result.rawTransaction = (0, _transactions.getRawTransaction)(movement);
}
return result;
})
};
}
/**
* one zero hebrew strings are reversed with a unicode control character that forces display in LTR order
* We need to remove the unicode control character, and then reverse hebrew substrings inside the string
*/
sanitizeHebrew(text) {
if (!text.includes('\u202d')) {
return text.trim();
}
const plainString = text.replace(/\u202d/gi, '').trim();
const hebrewSubStringsRanges = [...plainString.matchAll(HEBREW_WORDS_REGEX)];
const rangesToReverse = hebrewSubStringsRanges.map(str => ({
start: str.index,
end: str.index + str[0].length
}));
const out = [];
let index = 0;
for (const {
start,
end
} of rangesToReverse) {
out.push(...plainString.substring(index, start));
index += start - index;
const reversed = [...plainString.substring(start, end)].reverse();
out.push(...reversed);
index += end - start;
}
out.push(...plainString.substring(index, plainString.length));
return out.join('');
}
async fetchData() {
if (!this.accessToken) {
return (0, _errors.createGenericError)('login() was not called');
}
const defaultStartMoment = (0, _moment.default)().subtract(1, 'years').add(1, 'day');
const startDate = this.options.startDate || defaultStartMoment.toDate();
const startMoment = _moment.default.max(defaultStartMoment, (0, _moment.default)(startDate));
debug('Fetching account list');
const result = await (0, _fetch.fetchGraphql)(GRAPHQL_API_URL, _oneZeroQueries.GET_CUSTOMER, {}, {
authorization: `Bearer ${this.accessToken}`
});
const portfolios = result.customer.flatMap(customer => customer.portfolios || []);
return {
success: true,
accounts: await Promise.all(portfolios.map(portfolio => this.fetchPortfolioMovements(portfolio, startMoment.toDate())))
};
}
}
exports.default = OneZeroScraper;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbW9tZW50IiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfZGVidWciLCJfZmV0Y2giLCJfdHJhbnNhY3Rpb25zIiwiX3RyYW5zYWN0aW9uczIiLCJfYmFzZVNjcmFwZXIiLCJfZXJyb3JzIiwiX29uZVplcm9RdWVyaWVzIiwiZSIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiSEVCUkVXX1dPUkRTX1JFR0VYIiwiZGVidWciLCJnZXREZWJ1ZyIsIklERU5USVRZX1NFUlZFUl9VUkwiLCJHUkFQSFFMX0FQSV9VUkwiLCJPbmVaZXJvU2NyYXBlciIsIkJhc2VTY3JhcGVyIiwidHJpZ2dlclR3b0ZhY3RvckF1dGgiLCJwaG9uZU51bWJlciIsInN0YXJ0c1dpdGgiLCJjcmVhdGVHZW5lcmljRXJyb3IiLCJkZXZpY2VUb2tlblJlc3BvbnNlIiwiZmV0Y2hQb3N0IiwiZXh0Q2xpZW50SWQiLCJvcyIsInJlc3VsdERhdGEiLCJkZXZpY2VUb2tlbiIsIm90cFByZXBhcmVSZXNwb25zZSIsImZhY3RvclZhbHVlIiwib3RwQ2hhbm5lbCIsIm90cENvbnRleHQiLCJzdWNjZXNzIiwiZ2V0TG9uZ1Rlcm1Ud29GYWN0b3JUb2tlbiIsIm90cENvZGUiLCJvdHBWZXJpZnlSZXNwb25zZSIsIm90cFRva2VuIiwibG9uZ1Rlcm1Ud29GYWN0b3JBdXRoVG9rZW4iLCJyZXNvbHZlT3RwVG9rZW4iLCJjcmVkZW50aWFscyIsIm90cExvbmdUZXJtVG9rZW4iLCJvdHBDb2RlUmV0cmlldmVyIiwiZXJyb3JUeXBlIiwiU2NyYXBlckVycm9yVHlwZXMiLCJUd29GYWN0b3JSZXRyaWV2ZXJNaXNzaW5nIiwiZXJyb3JNZXNzYWdlIiwidHJpZ2dlclJlc3VsdCIsIm90cFRva2VuUmVzdWx0IiwibG9naW4iLCJnZXRJZFRva2VuUmVzcG9uc2UiLCJvdHBTbXNUb2tlbiIsImVtYWlsIiwicGFzcyIsInBhc3N3b3JkIiwicGluQ29kZSIsImlkVG9rZW4iLCJnZXRTZXNzaW9uVG9rZW5SZXNwb25zZSIsImFjY2Vzc1Rva2VuIiwicGVyc2lzdGVudE90cFRva2VuIiwiZmV0Y2hQb3J0Zm9saW9Nb3ZlbWVudHMiLCJwb3J0Zm9saW8iLCJzdGFydERhdGUiLCJhY2NvdW50IiwiYWNjb3VudHMiLCJjdXJzb3IiLCJtb3ZlbWVudHMiLCJsZW5ndGgiLCJEYXRlIiwibW92ZW1lbnRUaW1lc3RhbXAiLCJwb3J0Zm9saW9OdW0iLCJuZXdNb3ZlbWVudHMiLCJwYWdpbmF0aW9uIiwiZmV0Y2hHcmFwaHFsIiwiR0VUX01PVkVNRU5UUyIsInBvcnRmb2xpb0lkIiwiYWNjb3VudElkIiwibGFuZ3VhZ2UiLCJsaW1pdCIsImF1dGhvcml6YXRpb24iLCJ1bnNoaWZ0IiwiaGFzTW9yZSIsInNvcnQiLCJ4IiwieSIsInZhbHVlT2YiLCJtYXRjaGluZ01vdmVtZW50cyIsImZpbHRlciIsIm1vdmVtZW50IiwiYWNjb3VudE51bWJlciIsImJhbGFuY2UiLCJwYXJzZUZsb2F0IiwicnVubmluZ0JhbGFuY2UiLCJ0eG5zIiwibWFwIiwiaGFzSW5zdGFsbG1lbnRzIiwidHJhbnNhY3Rpb24iLCJlbnJpY2htZW50IiwicmVjdXJyZW5jZXMiLCJzb21lIiwiaXNSZWN1cnJlbnQiLCJtb2RpZmllciIsImNyZWRpdERlYml0IiwicmVzdWx0IiwiaWRlbnRpZmllciIsIm1vdmVtZW50SWQiLCJkYXRlIiwidmFsdWVEYXRlIiwiY2hhcmdlZEFtb3VudCIsIm1vdmVtZW50QW1vdW50IiwiY2hhcmdlZEN1cnJlbmN5IiwibW92ZW1lbnRDdXJyZW5jeSIsIm9yaWdpbmFsQW1vdW50Iiwib3JpZ2luYWxDdXJyZW5jeSIsImRlc2NyaXB0aW9uIiwic2FuaXRpemVIZWJyZXciLCJwcm9jZXNzZWREYXRlIiwic3RhdHVzIiwiVHJhbnNhY3Rpb25TdGF0dXNlcyIsIkNvbXBsZXRlZCIsInR5cGUiLCJUcmFuc2FjdGlvblR5cGVzIiwiSW5zdGFsbG1lbnRzIiwiTm9ybWFsIiwib3B0aW9ucyIsImluY2x1ZGVSYXdUcmFuc2FjdGlvbiIsInJhd1RyYW5zYWN0aW9uIiwiZ2V0UmF3VHJhbnNhY3Rpb24iLCJ0ZXh0IiwiaW5jbHVkZXMiLCJ0cmltIiwicGxhaW5TdHJpbmciLCJyZXBsYWNlIiwiaGVicmV3U3ViU3RyaW5nc1JhbmdlcyIsIm1hdGNoQWxsIiwicmFuZ2VzVG9SZXZlcnNlIiwic3RyIiwic3RhcnQiLCJpbmRleCIsImVuZCIsIm91dCIsInB1c2giLCJzdWJzdHJpbmciLCJyZXZlcnNlZCIsInJldmVyc2UiLCJqb2luIiwiZmV0Y2hEYXRhIiwiZGVmYXVsdFN0YXJ0TW9tZW50IiwibW9tZW50Iiwic3VidHJhY3QiLCJhZGQiLCJ0b0RhdGUiLCJzdGFydE1vbWVudCIsIm1heCIsIkdFVF9DVVNUT01FUiIsInBvcnRmb2xpb3MiLCJjdXN0b21lciIsImZsYXRNYXAiLCJQcm9taXNlIiwiYWxsIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zY3JhcGVycy9vbmUtemVyby50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgbW9tZW50IGZyb20gJ21vbWVudC9tb21lbnQnO1xuaW1wb3J0IHsgZ2V0RGVidWcgfSBmcm9tICcuLi9oZWxwZXJzL2RlYnVnJztcbmltcG9ydCB7IGZldGNoR3JhcGhxbCwgZmV0Y2hQb3N0IH0gZnJvbSAnLi4vaGVscGVycy9mZXRjaCc7XG5pbXBvcnQgeyBnZXRSYXdUcmFuc2FjdGlvbiB9IGZyb20gJy4uL2hlbHBlcnMvdHJhbnNhY3Rpb25zJztcbmltcG9ydCB7XG4gIHR5cGUgVHJhbnNhY3Rpb24gYXMgU2NyYXBpbmdUcmFuc2FjdGlvbixcbiAgVHJhbnNhY3Rpb25TdGF0dXNlcyxcbiAgVHJhbnNhY3Rpb25UeXBlcyxcbiAgdHlwZSBUcmFuc2FjdGlvbnNBY2NvdW50LFxufSBmcm9tICcuLi90cmFuc2FjdGlvbnMnO1xuaW1wb3J0IHsgQmFzZVNjcmFwZXIgfSBmcm9tICcuL2Jhc2Utc2NyYXBlcic7XG5pbXBvcnQgeyBTY3JhcGVyRXJyb3JUeXBlcywgY3JlYXRlR2VuZXJpY0Vycm9yIH0gZnJvbSAnLi9lcnJvcnMnO1xuaW1wb3J0IHtcbiAgdHlwZSBTY3JhcGVyR2V0TG9uZ1Rlcm1Ud29GYWN0b3JUb2tlblJlc3VsdCxcbiAgdHlwZSBTY3JhcGVyTG9naW5SZXN1bHQsXG4gIHR5cGUgU2NyYXBlclNjcmFwaW5nUmVzdWx0LFxuICB0eXBlIFNjcmFwZXJUd29GYWN0b3JBdXRoVHJpZ2dlclJlc3VsdCxcbn0gZnJvbSAnLi9pbnRlcmZhY2UnO1xuaW1wb3J0IHsgR0VUX0NVU1RPTUVSLCBHRVRfTU9WRU1FTlRTIH0gZnJvbSAnLi9vbmUtemVyby1xdWVyaWVzJztcblxuY29uc3QgSEVCUkVXX1dPUkRTX1JFR0VYID0gL1tcXHUwNTkwLVxcdTA1RkZdW1xcdTA1OTAtXFx1MDVGRlwiJ1xcLV8gL1xcXFxdKltcXHUwNTkwLVxcdTA1RkZdL2c7XG5cbmNvbnN0IGRlYnVnID0gZ2V0RGVidWcoJ29uZS16ZXJvJyk7XG5cbnR5cGUgQWNjb3VudCA9IHtcbiAgYWNjb3VudElkOiBzdHJpbmc7XG59O1xuXG50eXBlIFBvcnRmb2xpbyA9IHtcbiAgYWNjb3VudHM6IEFycmF5PEFjY291bnQ+O1xuICBwb3J0Zm9saW9JZDogc3RyaW5nO1xuICBwb3J0Zm9saW9OdW06IHN0cmluZztcbn07XG5cbnR5cGUgQ3VzdG9tZXIgPSB7XG4gIGN1c3RvbWVySWQ6IHN0cmluZztcbiAgcG9ydGZvbGlvcz86IEFycmF5PFBvcnRmb2xpbz4gfCBudWxsO1xufTtcblxuZXhwb3J0IHR5cGUgQ2F0ZWdvcnkgPSB7XG4gIGNhdGVnb3J5SWQ6IG51bWJlcjtcbiAgZGF0YVNvdXJjZTogc3RyaW5nO1xuICBzdWJDYXRlZ29yeUlkPzogbnVtYmVyIHwgbnVsbDtcbn07XG5cbmV4cG9ydCB0eXBlIFJlY3VycmVuY2UgPSB7XG4gIGRhdGFTb3VyY2U6IHN0cmluZztcbiAgaXNSZWN1cnJlbnQ6IGJvb2xlYW47XG59O1xuXG50eXBlIFRyYW5zYWN0aW9uRW5yaWNobWVudCA9IHtcbiAgY2F0ZWdvcmllcz86IENhdGVnb3J5W10gfCBudWxsO1xuICByZWN1cnJlbmNlcz86IFJlY3VycmVuY2VbXSB8IG51bGw7XG59O1xuXG50eXBlIFRyYW5zYWN0aW9uID0ge1xuICBlbnJpY2htZW50PzogVHJhbnNhY3Rpb25FbnJpY2htZW50IHwgbnVsbDtcbiAgLy8gVE9ETzogR2V0IGluc3RhbGxtZW50cyBpbmZvcm1hdGlvbiBoZXJlXG4gIC8vIHRyYW5zYWN0aW9uRGV0YWlsczogVHJhbnNhY3Rpb25EZXRhaWxzO1xufTtcblxudHlwZSBNb3ZlbWVudCA9IHtcbiAgYWNjb3VudElkOiBzdHJpbmc7XG4gIGJhbmtDdXJyZW5jeUFtb3VudDogc3RyaW5nO1xuICBib29raW5nRGF0ZTogc3RyaW5nO1xuICBjb252ZXJzaW9uUmF0ZTogc3RyaW5nO1xuICBjcmVkaXREZWJpdDogc3RyaW5nO1xuICBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICBpc1JldmVyc2VkOiBib29sZWFuO1xuICBtb3ZlbWVudEFtb3VudDogc3RyaW5nO1xuICBtb3ZlbWVudEN1cnJlbmN5OiBzdHJpbmc7XG4gIG1vdmVtZW50SWQ6IHN0cmluZztcbiAgbW92ZW1lbnRSZXZlcnNlZElkPzogc3RyaW5nIHwgbnVsbDtcbiAgbW92ZW1lbnRUaW1lc3RhbXA6IHN0cmluZztcbiAgbW92ZW1lbnRUeXBlOiBzdHJpbmc7XG4gIHBvcnRmb2xpb0lkOiBzdHJpbmc7XG4gIHJ1bm5pbmdCYWxhbmNlOiBzdHJpbmc7XG4gIHRyYW5zYWN0aW9uPzogVHJhbnNhY3Rpb24gfCBudWxsO1xuICB2YWx1ZURhdGU6IHN0cmluZztcbn07XG5cbnR5cGUgUXVlcnlQYWdpbmF0aW9uID0geyBoYXNNb3JlOiBib29sZWFuOyBjdXJzb3I6IHN0cmluZyB9O1xuXG5jb25zdCBJREVOVElUWV9TRVJWRVJfVVJMID0gJ2h0dHBzOi8vaWRlbnRpdHkudGZkLWJhbmsuY29tL3YxLyc7XG5cbmNvbnN0IEdSQVBIUUxfQVBJX1VSTCA9ICdodHRwczovL21vYmlsZS50ZmQtYmFuay5jb20vbW9iaWxlLWdyYXBoL2dyYXBocWwnO1xuXG50eXBlIFNjcmFwZXJTcGVjaWZpY0NyZWRlbnRpYWxzID0geyBlbWFpbDogc3RyaW5nOyBwYXNzd29yZDogc3RyaW5nIH0gJiAoXG4gIHwge1xuICAgICAgb3RwQ29kZVJldHJpZXZlcjogKCkgPT4gUHJvbWlzZTxzdHJpbmc+O1xuICAgICAgcGhvbmVOdW1iZXI6IHN0cmluZztcbiAgICB9XG4gIHwge1xuICAgICAgb3RwTG9uZ1Rlcm1Ub2tlbjogc3RyaW5nO1xuICAgIH1cbik7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE9uZVplcm9TY3JhcGVyIGV4dGVuZHMgQmFzZVNjcmFwZXI8U2NyYXBlclNwZWNpZmljQ3JlZGVudGlhbHM+IHtcbiAgcHJpdmF0ZSBvdHBDb250ZXh0Pzogc3RyaW5nO1xuXG4gIHByaXZhdGUgYWNjZXNzVG9rZW4/OiBzdHJpbmc7XG5cbiAgYXN5bmMgdHJpZ2dlclR3b0ZhY3RvckF1dGgocGhvbmVOdW1iZXI6IHN0cmluZyk6IFByb21pc2U8U2NyYXBlclR3b0ZhY3RvckF1dGhUcmlnZ2VyUmVzdWx0PiB7XG4gICAgaWYgKCFwaG9uZU51bWJlci5zdGFydHNXaXRoKCcrJykpIHtcbiAgICAgIHJldHVybiBjcmVhdGVHZW5lcmljRXJyb3IoXG4gICAgICAgICdBIGZ1bGwgaW50ZXJuYXRpb25hbCBwaG9uZSBudW1iZXIgc3RhcnRpbmcgd2l0aCArIGFuZCBhIHRocmVlIGRpZ2l0IGNvdW50cnkgY29kZSBpcyByZXF1aXJlZCcsXG4gICAgICApO1xuICAgIH1cblxuICAgIGRlYnVnKCdGZXRjaGluZyBkZXZpY2UgdG9rZW4nKTtcbiAgICBjb25zdCBkZXZpY2VUb2tlblJlc3BvbnNlID0gYXdhaXQgZmV0Y2hQb3N0KGAke0lERU5USVRZX1NFUlZFUl9VUkx9L2RldmljZXMvdG9rZW5gLCB7XG4gICAgICBleHRDbGllbnRJZDogJ21vYmlsZScsXG4gICAgICBvczogJ0FuZHJvaWQnLFxuICAgIH0pO1xuXG4gICAgY29uc3Qge1xuICAgICAgcmVzdWx0RGF0YTogeyBkZXZpY2VUb2tlbiB9LFxuICAgIH0gPSBkZXZpY2VUb2tlblJlc3BvbnNlO1xuXG4gICAgZGVidWcoYFNlbmRpbmcgT1RQIHRvIHBob25lIG51bWJlciAke3Bob25lTnVtYmVyfWApO1xuXG4gICAgY29uc3Qgb3RwUHJlcGFyZVJlc3BvbnNlID0gYXdhaXQgZmV0Y2hQb3N0KGAke0lERU5USVRZX1NFUlZFUl9VUkx9L290cC9wcmVwYXJlYCwge1xuICAgICAgZmFjdG9yVmFsdWU6IHBob25lTnVtYmVyLFxuICAgICAgZGV2aWNlVG9rZW4sXG4gICAgICBvdHBDaGFubmVsOiAnU01TX09UUCcsXG4gICAgfSk7XG5cbiAgICBjb25zdCB7XG4gICAgICByZXN1bHREYXRhOiB7IG90cENvbnRleHQgfSxcbiAgICB9ID0gb3RwUHJlcGFyZVJlc3BvbnNlO1xuXG4gICAgdGhpcy5vdHBDb250ZXh0ID0gb3RwQ29udGV4dDtcblxuICAgIHJldHVybiB7XG4gICAgICBzdWNjZXNzOiB0cnVlLFxuICAgIH07XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgZ2V0TG9uZ1Rlcm1Ud29GYWN0b3JUb2tlbihvdHBDb2RlOiBzdHJpbmcpOiBQcm9taXNlPFNjcmFwZXJHZXRMb25nVGVybVR3b0ZhY3RvclRva2VuUmVzdWx0PiB7XG4gICAgaWYgKCF0aGlzLm90cENvbnRleHQpIHtcbiAgICAgIHJldHVybiBjcmVhdGVHZW5lcmljRXJyb3IoJ3RyaWdnZXJPdHAgd2FzIG5vdCBjYWxsZWQgYmVmb3JlIGNhbGxpbmcgZ2V0UGVybWVuYW50T3RwVG9rZW4oKScpO1xuICAgIH1cblxuICAgIGRlYnVnKCdSZXF1ZXN0aW5nIE9UUCB0b2tlbicpO1xuICAgIGNvbnN0IG90cFZlcmlmeVJlc3BvbnNlID0gYXdhaXQgZmV0Y2hQb3N0KGAke0lERU5USVRZX1NFUlZFUl9VUkx9L290cC92ZXJpZnlgLCB7XG4gICAgICBvdHBDb250ZXh0OiB0aGlzLm90cENvbnRleHQsXG4gICAgICBvdHBDb2RlLFxuICAgIH0pO1xuXG4gICAgY29uc3Qge1xuICAgICAgcmVzdWx0RGF0YTogeyBvdHBUb2tlbiB9LFxuICAgIH0gPSBvdHBWZXJpZnlSZXNwb25zZTtcbiAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlLCBsb25nVGVybVR3b0ZhY3RvckF1dGhUb2tlbjogb3RwVG9rZW4gfTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVzb2x2ZU90cFRva2VuKFxuICAgIGNyZWRlbnRpYWxzOiBTY3JhcGVyU3BlY2lmaWNDcmVkZW50aWFscyxcbiAgKTogUHJvbWlzZTxTY3JhcGVyR2V0TG9uZ1Rlcm1Ud29GYWN0b3JUb2tlblJlc3VsdD4ge1xuICAgIGlmICgnb3RwTG9uZ1Rlcm1Ub2tlbicgaW4gY3JlZGVudGlhbHMpIHtcbiAgICAgIGlmICghY3JlZGVudGlhbHMub3RwTG9uZ1Rlcm1Ub2tlbikge1xuICAgICAgICByZXR1cm4gY3JlYXRlR2VuZXJpY0Vycm9yKCdJbnZhbGlkIG90cExvbmdUZXJtVG9rZW4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUsIGxvbmdUZXJtVHdvRmFjdG9yQXV0aFRva2VuOiBjcmVkZW50aWFscy5vdHBMb25nVGVybVRva2VuIH07XG4gICAgfVxuXG4gICAgaWYgKCFjcmVkZW50aWFscy5vdHBDb2RlUmV0cmlldmVyKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgZXJyb3JUeXBlOiBTY3JhcGVyRXJyb3JUeXBlcy5Ud29GYWN0b3JSZXRyaWV2ZXJNaXNzaW5nLFxuICAgICAgICBlcnJvck1lc3NhZ2U6ICdvdHBDb2RlUmV0cmlldmVyIGlzIHJlcXVpcmVkIHdoZW4gb3RwUGVybWFuZW50VG9rZW4gaXMgbm90IHByb3ZpZGVkJyxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKCFjcmVkZW50aWFscy5waG9uZU51bWJlcikge1xuICAgICAgcmV0dXJuIGNyZWF0ZUdlbmVyaWNFcnJvcigncGhvbmVOdW1iZXIgaXMgcmVxdWlyZWQgd2hlbiBwcm92aWRpbmcgYSBvdHBDb2RlUmV0cmlldmVyIGNhbGxiYWNrJyk7XG4gICAgfVxuXG4gICAgZGVidWcoJ1RyaWdnZXJpbmcgdXNlciBzdXBwbGllZCBvdHBDb2RlUmV0cmlldmVyIGNhbGxiYWNrJyk7XG4gICAgY29uc3QgdHJpZ2dlclJlc3VsdCA9IGF3YWl0IHRoaXMudHJpZ2dlclR3b0ZhY3RvckF1dGgoY3JlZGVudGlhbHMucGhvbmVOdW1iZXIpO1xuXG4gICAgaWYgKCF0cmlnZ2VyUmVzdWx0LnN1Y2Nlc3MpIHtcbiAgICAgIHJldHVybiB0cmlnZ2VyUmVzdWx0O1xuICAgIH1cblxuICAgIGNvbnN0IG90cENvZGUgPSBhd2FpdCBjcmVkZW50aWFscy5vdHBDb2RlUmV0cmlldmVyKCk7XG5cbiAgICBjb25zdCBvdHBUb2tlblJlc3VsdCA9IGF3YWl0IHRoaXMuZ2V0TG9uZ1Rlcm1Ud29GYWN0b3JUb2tlbihvdHBDb2RlKTtcbiAgICBpZiAoIW90cFRva2VuUmVzdWx0LnN1Y2Nlc3MpIHtcbiAgICAgIHJldHVybiBvdHBUb2tlblJlc3VsdDtcbiAgICB9XG5cbiAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlLCBsb25nVGVybVR3b0ZhY3RvckF1dGhUb2tlbjogb3RwVG9rZW5SZXN1bHQubG9uZ1Rlcm1Ud29GYWN0b3JBdXRoVG9rZW4gfTtcbiAgfVxuXG4gIGFzeW5jIGxvZ2luKGNyZWRlbnRpYWxzOiBTY3JhcGVyU3BlY2lmaWNDcmVkZW50aWFscyk6IFByb21pc2U8U2NyYXBlckxvZ2luUmVzdWx0PiB7XG4gICAgY29uc3Qgb3RwVG9rZW5SZXN1bHQgPSBhd2FpdCB0aGlzLnJlc29sdmVPdHBUb2tlbihjcmVkZW50aWFscyk7XG4gICAgaWYgKCFvdHBUb2tlblJlc3VsdC5zdWNjZXNzKSB7XG4gICAgICByZXR1cm4gb3RwVG9rZW5SZXN1bHQ7XG4gICAgfVxuXG4gICAgZGVidWcoJ1JlcXVlc3RpbmcgaWQgdG9rZW4nKTtcbiAgICBjb25zdCBnZXRJZFRva2VuUmVzcG9uc2UgPSBhd2FpdCBmZXRjaFBvc3QoYCR7SURFTlRJVFlfU0VSVkVSX1VSTH0vZ2V0SWRUb2tlbmAsIHtcbiAgICAgIG90cFNtc1Rva2VuOiBvdHBUb2tlblJlc3VsdC5sb25nVGVybVR3b0ZhY3RvckF1dGhUb2tlbixcbiAgICAgIGVtYWlsOiBjcmVkZW50aWFscy5lbWFpbCxcbiAgICAgIHBhc3M6IGNyZWRlbnRpYWxzLnBhc3N3b3JkLFxuICAgICAgcGluQ29kZTogJycsXG4gICAgfSk7XG5cbiAgICBjb25zdCB7XG4gICAgICByZXN1bHREYXRhOiB7IGlkVG9rZW4gfSxcbiAgICB9ID0gZ2V0SWRUb2tlblJlc3BvbnNlO1xuXG4gICAgZGVidWcoJ1JlcXVlc3Rpbmcgc2Vzc2lvbiB0b2tlbicpO1xuXG4gICAgY29uc3QgZ2V0U2Vzc2lvblRva2VuUmVzcG9uc2UgPSBhd2FpdCBmZXRjaFBvc3QoYCR7SURFTlRJVFlfU0VSVkVSX1VSTH0vc2Vzc2lvbnMvdG9rZW5gLCB7XG4gICAgICBpZFRva2VuLFxuICAgICAgcGFzczogY3JlZGVudGlhbHMucGFzc3dvcmQsXG4gICAgfSk7XG5cbiAgICBjb25zdCB7XG4gICAgICByZXN1bHREYXRhOiB7IGFjY2Vzc1Rva2VuIH0sXG4gICAgfSA9IGdldFNlc3Npb25Ub2tlblJlc3BvbnNlO1xuXG4gICAgdGhpcy5hY2Nlc3NUb2tlbiA9IGFjY2Vzc1Rva2VuO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBwZXJzaXN0ZW50T3RwVG9rZW46IG90cFRva2VuUmVzdWx0LmxvbmdUZXJtVHdvRmFjdG9yQXV0aFRva2VuLFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGZldGNoUG9ydGZvbGlvTW92ZW1lbnRzKHBvcnRmb2xpbzogUG9ydGZvbGlvLCBzdGFydERhdGU6IERhdGUpOiBQcm9taXNlPFRyYW5zYWN0aW9uc0FjY291bnQ+IHtcbiAgICAvLyBUT0RPOiBGaW5kIG91dCBpZiB3ZSBuZWVkIHRoZSBvdGhlciBhY2NvdW50cywgdGhlcmUgc2VlbXMgdG8gYWx3YXlzIGJlIG9uZVxuICAgIGNvbnN0IGFjY291bnQgPSBwb3J0Zm9saW8uYWNjb3VudHNbMF07XG4gICAgbGV0IGN1cnNvciA9IG51bGw7XG4gICAgY29uc3QgbW92ZW1lbnRzID0gW107XG5cbiAgICB3aGlsZSAoIW1vdmVtZW50cy5sZW5ndGggfHwgbmV3IERhdGUobW92ZW1lbnRzWzBdLm1vdmVtZW50VGltZXN0YW1wKSA+PSBzdGFydERhdGUpIHtcbiAgICAgIGRlYnVnKGBGZXRjaGluZyB0cmFuc2FjdGlvbnMgZm9yIGFjY291bnQgJHtwb3J0Zm9saW8ucG9ydGZvbGlvTnVtfS4uLmApO1xuICAgICAgY29uc3Qge1xuICAgICAgICBtb3ZlbWVudHM6IHsgbW92ZW1lbnRzOiBuZXdNb3ZlbWVudHMsIHBhZ2luYXRpb24gfSxcbiAgICAgIH06IHsgbW92ZW1lbnRzOiB7IG1vdmVtZW50czogTW92ZW1lbnRbXTsgcGFnaW5hdGlvbjogUXVlcnlQYWdpbmF0aW9uIH0gfSA9IGF3YWl0IGZldGNoR3JhcGhxbChcbiAgICAgICAgR1JBUEhRTF9BUElfVVJMLFxuICAgICAgICBHRVRfTU9WRU1FTlRTLFxuICAgICAgICB7XG4gICAgICAgICAgcG9ydGZvbGlvSWQ6IHBvcnRmb2xpby5wb3J0Zm9saW9JZCxcbiAgICAgICAgICBhY2NvdW50SWQ6IGFjY291bnQuYWNjb3VudElkLFxuICAgICAgICAgIGxhbmd1YWdlOiAnSEVCUkVXJyxcbiAgICAgICAgICBwYWdpbmF0aW9uOiB7XG4gICAgICAgICAgICBjdXJzb3IsXG4gICAgICAgICAgICBsaW1pdDogNTAsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgICAgeyBhdXRob3JpemF0aW9uOiBgQmVhcmVyICR7dGhpcy5hY2Nlc3NUb2tlbn1gIH0sXG4gICAgICApO1xuXG4gICAgICBtb3ZlbWVudHMudW5zaGlmdCguLi5uZXdNb3ZlbWVudHMpO1xuICAgICAgY3Vyc29yID0gcGFnaW5hdGlvbi5jdXJzb3I7XG4gICAgICBpZiAoIXBhZ2luYXRpb24uaGFzTW9yZSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBtb3ZlbWVudHMuc29ydCgoeCwgeSkgPT4gbmV3IERhdGUoeC5tb3ZlbWVudFRpbWVzdGFtcCkudmFsdWVPZigpIC0gbmV3IERhdGUoeS5tb3ZlbWVudFRpbWVzdGFtcCkudmFsdWVPZigpKTtcblxuICAgIGNvbnN0IG1hdGNoaW5nTW92ZW1lbnRzID0gbW92ZW1lbnRzLmZpbHRlcihtb3ZlbWVudCA9PiBuZXcgRGF0ZShtb3ZlbWVudC5tb3ZlbWVudFRpbWVzdGFtcCkgPj0gc3RhcnREYXRlKTtcbiAgICByZXR1cm4ge1xuICAgICAgYWNjb3VudE51bWJlcjogcG9ydGZvbGlvLnBvcnRmb2xpb051bSxcbiAgICAgIGJhbGFuY2U6ICFtb3ZlbWVudHMubGVuZ3RoID8gMCA6IHBhcnNlRmxvYXQobW92ZW1lbnRzW21vdmVtZW50cy5sZW5ndGggLSAxXS5ydW5uaW5nQmFsYW5jZSksXG4gICAgICB0eG5zOiBtYXRjaGluZ01vdmVtZW50cy5tYXAoKG1vdmVtZW50KTogU2NyYXBpbmdUcmFuc2FjdGlvbiA9PiB7XG4gICAgICAgIGNvbnN0IGhhc0luc3RhbGxtZW50cyA9IG1vdmVtZW50LnRyYW5zYWN0aW9uPy5lbnJpY2htZW50Py5yZWN1cnJlbmNlcz8uc29tZSh4ID0+IHguaXNSZWN1cnJlbnQpO1xuICAgICAgICBjb25zdCBtb2RpZmllciA9IG1vdmVtZW50LmNyZWRpdERlYml0ID09PSAnREVCSVQnID8gLTEgOiAxO1xuICAgICAgICBjb25zdCByZXN1bHQ6IFNjcmFwaW5nVHJhbnNhY3Rpb24gPSB7XG4gICAgICAgICAgaWRlbnRpZmllcjogbW92ZW1lbnQubW92ZW1lbnRJZCxcbiAgICAgICAgICBkYXRlOiBtb3ZlbWVudC52YWx1ZURhdGUsXG4gICAgICAgICAgY2hhcmdlZEFtb3VudDogK21vdmVtZW50Lm1vdmVtZW50QW1vdW50ICogbW9kaWZpZXIsXG4gICAgICAgICAgY2hhcmdlZEN1cnJlbmN5OiBtb3ZlbWVudC5tb3ZlbWVudEN1cnJlbmN5LFxuICAgICAgICAgIG9yaWdpbmFsQW1vdW50OiArbW92ZW1lbnQubW92ZW1lbnRBbW91bnQgKiBtb2RpZmllcixcbiAgICAgICAgICBvcmlnaW5hbEN1cnJlbmN5OiBtb3ZlbWVudC5tb3ZlbWVudEN1cnJlbmN5LFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiB0aGlzLnNhbml0aXplSGVicmV3KG1vdmVtZW50LmRlc2NyaXB0aW9uKSxcbiAgICAgICAgICBwcm9jZXNzZWREYXRlOiBtb3ZlbWVudC5tb3ZlbWVudFRpbWVzdGFtcCxcbiAgICAgICAgICBzdGF0dXM6IFRyYW5zYWN0aW9uU3RhdHVzZXMuQ29tcGxldGVkLFxuICAgICAgICAgIHR5cGU6IGhhc0luc3RhbGxtZW50cyA/IFRyYW5zYWN0aW9uVHlwZXMuSW5zdGFsbG1lbnRzIDogVHJhbnNhY3Rpb25UeXBlcy5Ob3JtYWwsXG4gICAgICAgIH07XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9ucz8uaW5jbHVkZVJhd1RyYW5zYWN0aW9uKSB7XG4gICAgICAgICAgcmVzdWx0LnJhd1RyYW5zYWN0aW9uID0gZ2V0UmF3VHJhbnNhY3Rpb24obW92ZW1lbnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH0pLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogb25lIHplcm8gaGVicmV3IHN0cmluZ3MgYXJlIHJldmVyc2VkIHdpdGggYSB1bmljb2RlIGNvbnRyb2wgY2hhcmFjdGVyIHRoYXQgZm9yY2VzIGRpc3BsYXkgaW4gTFRSIG9yZGVyXG4gICAqIFdlIG5lZWQgdG8gcmVtb3ZlIHRoZSB1bmljb2RlIGNvbnRyb2wgY2hhcmFjdGVyLCBhbmQgdGhlbiByZXZlcnNlIGhlYnJldyBzdWJzdHJpbmdzIGluc2lkZSB0aGUgc3RyaW5nXG4gICAqL1xuICBwcml2YXRlIHNhbml0aXplSGVicmV3KHRleHQ6IHN0cmluZykge1xuICAgIGlmICghdGV4dC5pbmNsdWRlcygnXFx1MjAyZCcpKSB7XG4gICAgICByZXR1cm4gdGV4dC50cmltKCk7XG4gICAgfVxuXG4gICAgY29uc3QgcGxhaW5TdHJpbmcgPSB0ZXh0LnJlcGxhY2UoL1xcdTIwMmQvZ2ksICcnKS50cmltKCk7XG4gICAgY29uc3QgaGVicmV3U3ViU3RyaW5nc1JhbmdlcyA9IFsuLi5wbGFpblN0cmluZy5tYXRjaEFsbChIRUJSRVdfV09SRFNfUkVHRVgpXTtcbiAgICBjb25zdCByYW5nZXNUb1JldmVyc2UgPSBoZWJyZXdTdWJTdHJpbmdzUmFuZ2VzLm1hcChzdHIgPT4gKHsgc3RhcnQ6IHN0ci5pbmRleCEsIGVuZDogc3RyLmluZGV4ISArIHN0clswXS5sZW5ndGggfSkpO1xuICAgIGNvbnN0IG91dCA9IFtdO1xuICAgIGxldCBpbmRleCA9IDA7XG5cbiAgICBmb3IgKGNvbnN0IHsgc3RhcnQsIGVuZCB9IG9mIHJhbmdlc1RvUmV2ZXJzZSkge1xuICAgICAgb3V0LnB1c2goLi4ucGxhaW5TdHJpbmcuc3Vic3RyaW5nKGluZGV4LCBzdGFydCkpO1xuICAgICAgaW5kZXggKz0gc3RhcnQgLSBpbmRleDtcbiAgICAgIGNvbnN0IHJldmVyc2VkID0gWy4uLnBsYWluU3RyaW5nLnN1YnN0cmluZyhzdGFydCwgZW5kKV0ucmV2ZXJzZSgpO1xuICAgICAgb3V0LnB1c2goLi4ucmV2ZXJzZWQpO1xuICAgICAgaW5kZXggKz0gZW5kIC0gc3RhcnQ7XG4gICAgfVxuXG4gICAgb3V0LnB1c2goLi4ucGxhaW5TdHJpbmcuc3Vic3RyaW5nKGluZGV4LCBwbGFpblN0cmluZy5sZW5ndGgpKTtcblxuICAgIHJldHVybiBvdXQuam9pbignJyk7XG4gIH1cblxuICBhc3luYyBmZXRjaERhdGEoKTogUHJvbWlzZTxTY3JhcGVyU2NyYXBpbmdSZXN1bHQ+IHtcbiAgICBpZiAoIXRoaXMuYWNjZXNzVG9rZW4pIHtcbiAgICAgIHJldHVybiBjcmVhdGVHZW5lcmljRXJyb3IoJ2xvZ2luKCkgd2FzIG5vdCBjYWxsZWQnKTtcbiAgICB9XG5cbiAgICBjb25zdCBkZWZhdWx0U3RhcnRNb21lbnQgPSBtb21lbnQoKS5zdWJ0cmFjdCgxLCAneWVhcnMnKS5hZGQoMSwgJ2RheScpO1xuICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IHRoaXMub3B0aW9ucy5zdGFydERhdGUgfHwgZGVmYXVsdFN0YXJ0TW9tZW50LnRvRGF0ZSgpO1xuICAgIGNvbnN0IHN0YXJ0TW9tZW50ID0gbW9tZW50Lm1heChkZWZhdWx0U3RhcnRNb21lbnQsIG1vbWVudChzdGFydERhdGUpKTtcblxuICAgIGRlYnVnKCdGZXRjaGluZyBhY2NvdW50IGxpc3QnKTtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBmZXRjaEdyYXBocWw8eyBjdXN0b21lcjogQ3VzdG9tZXJbXSB9PihcbiAgICAgIEdSQVBIUUxfQVBJX1VSTCxcbiAgICAgIEdFVF9DVVNUT01FUixcbiAgICAgIHt9LFxuICAgICAgeyBhdXRob3JpemF0aW9uOiBgQmVhcmVyICR7dGhpcy5hY2Nlc3NUb2tlbn1gIH0sXG4gICAgKTtcbiAgICBjb25zdCBwb3J0Zm9saW9zID0gcmVzdWx0LmN1c3RvbWVyLmZsYXRNYXAoY3VzdG9tZXIgPT4gY3VzdG9tZXIucG9ydGZvbGlvcyB8fCBbXSk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIGFjY291bnRzOiBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgcG9ydGZvbGlvcy5tYXAocG9ydGZvbGlvID0+IHRoaXMuZmV0Y2hQb3J0Zm9saW9Nb3ZlbWVudHMocG9ydGZvbGlvLCBzdGFydE1vbWVudC50b0RhdGUoKSkpLFxuICAgICAgKSxcbiAgICB9O1xuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLElBQUFBLE9BQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLE1BQUEsR0FBQUQsT0FBQTtBQUNBLElBQUFFLE1BQUEsR0FBQUYsT0FBQTtBQUNB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