Skip to content
Snippets Groups Projects
index.js 8.18 MiB
Newer Older
Hugo NOUTS's avatar
Hugo NOUTS committed
/******/ (() => { // webpackBootstrap
/******/ 	var __webpack_modules__ = ([
/* 0 */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

// @ts-check
const {
  BaseKonnector,
  log,
  hydrateAndFilter,
  addData,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  errors,
Hugo NOUTS's avatar
Hugo NOUTS committed
} = __webpack_require__(1)
const soapRequest = __webpack_require__(1337)
const moment = __webpack_require__(1379)
__webpack_require__(1516)
const xml2js = __webpack_require__(1519)
const { buildAggregatedData } = __webpack_require__(1560)
Hugo NOUTS's avatar
Hugo NOUTS committed
const {
  parseSgeXmlData,
  formateDataForDoctype,
  parseTags,
  parseValue,
} = __webpack_require__(1561)
build-token's avatar
build-token committed
const {
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  consultationMesuresDetailleesMaxPower,
  consultationMesuresDetaillees,
} = __webpack_require__(1562)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
const {
  updateBoConsent,
  createBoConsent,
  getBoConsent,
  deleteBoConsent,
} = __webpack_require__(1563)
const {
  verifyUserIdentity,
  activateContract,
  verifyContract,
  terminateContract,
  getContractStartDate,
} = __webpack_require__(1681)
const { getAccount, saveAccountData } = __webpack_require__(1691)
const { isLocal, isDev } = __webpack_require__(1692)
const Sentry = __webpack_require__(1601)
// eslint-disable-next-line
const Tracing = __webpack_require__(1693) // Needed for tracking performance in Sentry
const { version } = __webpack_require__(1730)
Hugo SUBTIL's avatar
Hugo SUBTIL committed

Hugo NOUTS's avatar
Hugo NOUTS committed
moment.locale('fr') // set the language
moment.tz.setDefault('Europe/Paris') // set the timezone

/*** Connector Constants ***/
const manualExecution =
  process.env.COZY_JOB_MANUAL_EXECUTION === 'true' ? true : false
let startDailyDate = manualExecution
  ? moment().subtract(12, 'month')
  : moment().subtract(6, 'month')
let startDailyDateString = startDailyDate.format('YYYY-MM-DD')
const startLoadDate = moment().subtract(7, 'day')
const endDate = moment()
const endDateString = endDate.format('YYYY-MM-DD')
const ACCOUNT_ID = isLocal() ? 'default_account_id' : 'enedissgegrandlyon'
Hugo NOUTS's avatar
Hugo NOUTS committed

module.exports = new BaseKonnector(start)

/**
 * Sentry
 */
Sentry.init({
  dsn:
    'https://18747a93401447f2a81b83cd8c4bbbdf@grandlyon.errors.cozycloud.cc/5',

  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  tracesSampleRate: isLocal() ? 0 : 1.0,
  release: version,
  environment: isDev() ? 'development' : 'production',
  debug: isDev(),
  integrations: [
    // enable HTTP calls tracing
    new Sentry.Integrations.Http({ tracing: true }),
  ],
})

Hugo SUBTIL's avatar
Hugo SUBTIL committed
/**
 * The start function is run by the BaseKonnector instance only when it got all the account
 * information (fields). When you run this connector yourself in "standalone" mode or "dev" mode,
 * the account information come from ./konnector-dev-config.json file
 * cozyParameters are static parameters, independents from the account. Most often, it can be a
 * secret api key.
 * @param {fields} fields
 * @param {{secret: fields}} cozyParameters
 */
Hugo NOUTS's avatar
Hugo NOUTS committed
async function start(fields, cozyParameters) {
  try {
    log('info', 'Konnector configuration ...')
    log('info', `isManual execution: ${manualExecution}`)
    const transaction = Sentry.startTransaction({
      op: 'konnector',
      name: 'SGE Konnector',
    })
Hugo SUBTIL's avatar
Hugo SUBTIL committed

    const pointId = parseInt(fields.pointId)
    let baseUrl = fields.wso2BaseUrl
    let apiAuthKey = fields.apiToken
    let contractId = fields.contractId
    let sgeLogin = fields.sgeLogin
    let boToken = fields.boToken
    let boBaseUrl = fields.boBaseUrl
    if (cozyParameters && Object.keys(cozyParameters).length !== 0) {
      log('debug', 'Found COZY_PARAMETERS')
      baseUrl = cozyParameters.secret.wso2BaseUrl
      apiAuthKey = cozyParameters.secret.apiToken
      contractId = cozyParameters.secret.contractId
      sgeLogin = cozyParameters.secret.sgeLogin
      boBaseUrl = cozyParameters.secret.boBaseUrl
      boToken = cozyParameters.secret.boToken
    }

    // Prevent missing configuration
    if (
      !baseUrl ||
      !apiAuthKey ||
      !contractId ||
      !sgeLogin ||
      !boToken ||
      !boBaseUrl
    ) {
      const errorMessage = 'Missing configuration secrets'
      log('error', errorMessage)
      Sentry.captureException(errorMessage)
      throw errors.VENDOR_DOWN
    }
    /**
     * If it's first start we have to do the following operations:
     * - verify pdl are matching
     * - BO: create backoffice consent
     * - get contract start date and store it
     * - activate half-hour
     * - BO: update consent with service ID
     */
    log('info', 'User Logging...')
    if (isFirstStart(await getAccount(ACCOUNT_ID))) {
      log('info', 'First start...')
      transaction.startChild({ op: 'First start' })
      const user = await verifyUserIdentity(
        fields,
        baseUrl,
        apiAuthKey,
        sgeLogin
      )
Hugo SUBTIL's avatar
Hugo SUBTIL committed

      let consent = await createBoConsent(
        boBaseUrl,
        boToken,
        pointId,
        user.lastname,
        user.firstname,
        user.address,
        user.postalCode,
        user.inseeCode,
        user.city,
        user.hasBeenThroughtSafetyOnBoarding
      )
Hugo SUBTIL's avatar
Hugo SUBTIL committed

      // handle user contract start date in order to properly request data
      const userContractStartDate = await getContractStartDate(
Hugo SUBTIL's avatar
Hugo SUBTIL committed
        baseUrl,
        apiAuthKey,
        sgeLogin,
        pointId
      startDailyDate = moment(userContractStartDate, 'YYYY-MM-DD')
      startDailyDateString = startDailyDate.format('YYYY-MM-DD')
      const contractStartDate = moment().format('YYYY-MM-DD')
      const contractEndDate = moment()
        .add(1, 'year') // SGE force 1 year duration
        .format('YYYY-MM-DD')

      let serviceId = await verifyContract(
Hugo SUBTIL's avatar
Hugo SUBTIL committed
        baseUrl,
        apiAuthKey,
        sgeLogin,
        contractId,
        user.pointId
      )
      if (!serviceId) {
        serviceId = await activateContract(
          baseUrl,
          apiAuthKey,
          sgeLogin,
          contractId,
          user.lastname,
          user.pointId,
          contractStartDate,
          contractEndDate
        )
      }
      consent = await updateBoConsent(
Hugo SUBTIL's avatar
Hugo SUBTIL committed
        boBaseUrl,
        consent,
        serviceId.toString()
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      )
      // Save bo id into account
      const accountData = await getAccount(ACCOUNT_ID)

      await saveAccountData(ACCOUNT_ID, {
        ...accountData.data,
        consentId: consent.ID,
        expirationDate: contractEndDate,
        inseeCode: user.inseeCode,
      })
    } else {
      log('info', 'Alternate start...')
      transaction.startChild({ op: 'Alternate start' })
      const accountData = await getAccount(ACCOUNT_ID)
      const userConsent = await getBoConsent(
        boBaseUrl,
        boToken,
        accountData.data.consentId
      )
      const user = await verifyUserIdentity(
        fields,
        baseUrl,
        apiAuthKey,
        sgeLogin,
        true,
        accountData.data.inseeCode
      )

      if (!userConsent) {
        const errorMessage = 'No user consent found'
        log('error', errorMessage)
        Sentry.captureException(errorMessage)
        throw errors.VENDOR_DOWN
      }

      const consentEndDate = Date.parse(userConsent.endDate)
      const today = Date.now()
      if (
        user.lastname.toLocaleUpperCase() !==
          userConsent.lastname.toLocaleUpperCase() ||
        !user ||
        consentEndDate < today
      ) {
        await deleteConsent(
          userConsent,
          baseUrl,
          apiAuthKey,
          sgeLogin,
          contractId,
          pointId,
          boBaseUrl,
          boToken,
          consentEndDate < today
        )
      }
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    }
    log('info', 'Successfully logged in')
    await gatherData(baseUrl, apiAuthKey, sgeLogin, pointId)
Hugo NOUTS's avatar
Hugo NOUTS committed

    transaction.finish()
    log('info', 'Konnector success')
  } catch (error) {
    log('debug', 'error catched in start()', error)
    await Sentry.flush()
    throw error
  }
Hugo SUBTIL's avatar
Hugo SUBTIL committed
}

/**
 * Delete User Consent
 * @param {Consent} userConsent
 * @param {string} baseUrl
 * @param {string} apiAuthKey
 * @param {string} sgeLogin
 * @param {string} contractId
 * @param {number} pointId
 * @param {string} boBaseUrl
 * @param {string} boToken
 * @param {boolean} isConsentExpired
Hugo SUBTIL's avatar
Hugo SUBTIL committed
 */
async function deleteConsent(
  userConsent,
  baseUrl,
  apiAuthKey,
  sgeLogin,
  contractId,
  pointId,
  boBaseUrl,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
) {
  log('error', `Invalid or not found consent for user`)
  Sentry.captureMessage(`Invalid or not found consent for user`)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  if (userConsent.serviceID) {
    await terminateContract(
      baseUrl,
      apiAuthKey,
      sgeLogin,
      contractId,
      pointId,
      userConsent.serviceID
    )
    await deleteBoConsent(boBaseUrl, boToken, userConsent.ID || 0)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  } else {
    const errorMessage = `No service id retrieved from BO`
    log('error', errorMessage)
    Sentry.captureException(errorMessage)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    throw errors.VENDOR_DOWN
  }
    Sentry.captureException('Consent expired')
    throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED
  }
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  throw errors.TERMS_VERSION_MISMATCH
Hugo SUBTIL's avatar
Hugo SUBTIL committed
}

/**
 * Main method for gathering data
 * @param {string} baseUrl
 * @param {string} apiAuthKey
 * @param {string} sgeLogin
 * @param {number} pointId
 */
async function gatherData(baseUrl, apiAuthKey, sgeLogin, pointId) {
Hugo NOUTS's avatar
Hugo NOUTS committed
  log('info', 'Querying data...')
  const userContractStartDate = await getContractStartDate(
  startDailyDate = moment(userContractStartDate, 'YYYY-MM-DD')
  startDailyDateString = startDailyDate.format('YYYY-MM-DD')

Hugo NOUTS's avatar
Hugo NOUTS committed
  await getData(
    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
    apiAuthKey,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    sgeLogin,
    pointId
Hugo NOUTS's avatar
Hugo NOUTS committed
  )
  await getMaxPowerData(
    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
    apiAuthKey,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    sgeLogin,
    pointId
Hugo NOUTS's avatar
Hugo NOUTS committed
  )
  await getDataHalfHour(
    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
    apiAuthKey,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    sgeLogin,
    pointId
Hugo NOUTS's avatar
Hugo NOUTS committed
  )
  log('info', 'Querying data: done')
}
Hugo SUBTIL's avatar
Hugo SUBTIL committed

Hugo NOUTS's avatar
Hugo NOUTS committed
/**
 * Get hour data
 * @param {string} url
 * @param {string} apiAuthKey
 * @param {string} userLogin
 * @param {number} pointId
 */
async function getData(url, apiAuthKey, userLogin, pointId) {
  log('info', 'Fetching data')
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  const sgeHeaders = {
Hugo NOUTS's avatar
Hugo NOUTS committed
    'Content-Type': 'text/xml;charset=UTF-8',
    apikey: apiAuthKey,
  }

Hugo NOUTS's avatar
Hugo NOUTS committed

  const { response } = await soapRequest({
    url: url,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    headers: sgeHeaders,
    xml: consultationMesuresDetaillees(
Hugo NOUTS's avatar
Hugo NOUTS committed
      pointId,
      userLogin,
      startDailyDateString,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      endDateString,
      'ENERGIE',
      'EA'
Hugo NOUTS's avatar
Hugo NOUTS committed
    ),
  }).catch(err => {
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    log('error', 'consultationMesuresDetaillees')
Hugo NOUTS's avatar
Hugo NOUTS committed
    log('error', err)
    Sentry.captureException('consultationMesuresDetaillees:', err)
Hugo NOUTS's avatar
Hugo NOUTS committed
    return err
  })

  xml2js.parseString(
    response.body,
    {
      tagNameProcessors: [parseTags],
      valueProcessors: [parseValue],
      explicitArray: false,
    },
    processData()
  )
}

/**
 * Get Max power data
 * @param {string} url
 * @param {string} apiAuthKey
 * @param {string} userLogin
 * @param {number} pointId
 */
async function getMaxPowerData(url, apiAuthKey, userLogin, pointId) {
  log('info', 'Fetching Max Power data')
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  const sgeHeaders = {
Hugo NOUTS's avatar
Hugo NOUTS committed
    'Content-Type': 'text/xml;charset=UTF-8',
    apikey: apiAuthKey,
  }

Hugo NOUTS's avatar
Hugo NOUTS committed

  const { response } = await soapRequest({
    url: url,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    headers: sgeHeaders,
    xml: consultationMesuresDetailleesMaxPower(
      pointId,
      userLogin,
      startDailyDateString,
      endDateString
    ),
Hugo NOUTS's avatar
Hugo NOUTS committed
  }).catch(err => {
    log('error', 'getMaxPowerData')
    log('error', err)
    Sentry.captureException('getMaxPowerDate')
Hugo NOUTS's avatar
Hugo NOUTS committed
    return err
  })

  xml2js.parseString(
    response.body,
    {
      tagNameProcessors: [parseTags],
      valueProcessors: [parseValue],
      explicitArray: false,
    },
    processData('com.grandlyon.enedis.maxpower')
  )
}

/**
 * If start date exceed the maximum amount of data we can get with one query
 * get only 36 month. Or 12 month if manual execution
 * On manual execution, set the start date to one year ago.
 */
function limitStartDate() {
  const livingDuration = moment(endDate).diff(startDailyDate, 'months', true)
  // We need to prevent case that there is less than 12 month data
  if (manualExecution && livingDuration > 12) {
    startDailyDate = moment(endDate).subtract(12, 'month')
    startDailyDateString = startDailyDate.format('YYYY-MM-DD')
  } else if (livingDuration > 36) {
Hugo NOUTS's avatar
Hugo NOUTS committed
    log(
      'info',
      'Start date exceed 36 month, setting start date to current date minus 36 month'
    )
    startDailyDate = moment(endDate).subtract(36, 'month')
    startDailyDateString = startDailyDate.format('YYYY-MM-DD')
  }
}

/**
 * Get half-hour data
 * @param {string} url
 * @param {string} apiAuthKey
 * @param {string} userLogin
 * @param {number} pointId
 */
async function getDataHalfHour(url, apiAuthKey, userLogin, pointId) {
  log('info', 'Fetching data')
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  const sgeHeaders = {
Hugo NOUTS's avatar
Hugo NOUTS committed
    'Content-Type': 'text/xml;charset=UTF-8',
    apikey: apiAuthKey,
  }

  // If manual execution, retrieve only 1 week otherwise retrieve 4 weeks
  const MAX_HISTO = manualExecution ? 1 : 4

Hugo NOUTS's avatar
Hugo NOUTS committed
  for (var i = 0; i < MAX_HISTO; i++) {
    log('info', 'launch process with history')
    const incrementedStartDateString = moment(startLoadDate)
Hugo NOUTS's avatar
Hugo NOUTS committed
      .subtract(7 * i, 'day')
      .format('YYYY-MM-DD')
    const incrementedEndDateString = moment(endDate)
      .subtract(7 * i, 'day')
      .format('YYYY-MM-DD')
Hugo NOUTS's avatar
Hugo NOUTS committed
    const { response } = await soapRequest({
      url: url,
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      headers: sgeHeaders,
      xml: consultationMesuresDetaillees(
Hugo NOUTS's avatar
Hugo NOUTS committed
        pointId,
        userLogin,
        incrementedStartDateString,
Hugo NOUTS's avatar
Hugo NOUTS committed
        incrementedEndDateString,
        'COURBE',
        'PA'
      ),
    }).catch(err => {
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      log('error', 'consultationMesuresDetaillees half-hour')
Hugo NOUTS's avatar
Hugo NOUTS committed
      log('error', err)
      Sentry.captureException('consultationMesuresDetaillees half-hour')
Hugo NOUTS's avatar
Hugo NOUTS committed
      return err
    })

    xml2js.parseString(
      response.body,
      {
        tagNameProcessors: [parseTags],
Hugo NOUTS's avatar
Hugo NOUTS committed
        explicitArray: false,
      },
      processData('com.grandlyon.enedis.minute')
    )
  }
}

/**
 * Parse data
 * @param {string} doctype
 * @returns
 */
function processData(doctype = 'com.grandlyon.enedis.day') {
  return async (err, result) => {
    if (err) {
      log('error', err)
      Sentry.captureException('error while processing daily data')
Hugo NOUTS's avatar
Hugo NOUTS committed
      throw err
    }
    // Return only needed part of info
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    log('info', doctype)
    try {
      const data = parseSgeXmlData(result)
      const processedDailyData = await storeData(
        await formateDataForDoctype(data),
        doctype,
        ['year', 'month', 'day', 'hour', 'minute']
      )
Hugo NOUTS's avatar
Hugo NOUTS committed

      log('info', 'Aggregate enedis daily data for month and year')
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      if (doctype === 'com.grandlyon.enedis.day') {
        await aggregateMonthAndYearData(processedDailyData)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      }
    } catch (e) {
      if (doctype === 'com.grandlyon.enedis.minute') {
        const errorMessage = `No half-hour activated. Issue: ${result.Envelope.Body.Fault.faultstring}`
        Sentry.captureMessage(errorMessage)
        log('warn', errorMessage)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      } else {
        log('warn', `Unknown error ${e}`)
Hugo SUBTIL's avatar
Hugo SUBTIL committed
      }
Hugo NOUTS's avatar
Hugo NOUTS committed
    }
  }
}

/**
 * Save data in the right doctype db and prevent duplicated keys
 * @param {EnedisKonnectorData[]} data
 * @param {string} doctype
 * @param {string[]} filterKeys
 * @returns {Promise<*>}
 */
async function storeData(data, doctype, filterKeys) {
  log('debug', doctype, 'Store into')
  const filteredDocuments = await hydrateAndFilter(data, doctype, {
    keys: filterKeys,
  })
  await addData(filteredDocuments, doctype)
  return filteredDocuments
}

/**
 * Aggregate data from daily data to monthly and yearly data
Hugo NOUTS's avatar
Hugo NOUTS committed
 */
async function aggregateMonthAndYearData(data) {
Hugo NOUTS's avatar
Hugo NOUTS committed
  // Sum year and month values into object with year or year-month as keys
  if (data && data.length > 0) {
    let monthData = {}
    let yearData = {}
    data.forEach(element => {
      element.year + '-' + element.month in monthData
        ? (monthData[element.year + '-' + element.month] += element.load)
        : (monthData[element.year + '-' + element.month] = element.load)
      element.year in yearData
        ? (yearData[element.year] += element.load)
        : (yearData[element.year] = element.load)
    })
    // Aggregation for Month data
    const aggregatedMonthData = await buildAggregatedData(
Hugo NOUTS's avatar
Hugo NOUTS committed
      monthData,
      'com.grandlyon.enedis.month'
    )
    await storeData(aggregatedMonthData, 'com.grandlyon.enedis.month', [
Hugo NOUTS's avatar
Hugo NOUTS committed
      'year',
      'month',
    ])
    // Aggregation for Year data
    const aggregatedYearData = await buildAggregatedData(
Hugo NOUTS's avatar
Hugo NOUTS committed
      yearData,
      'com.grandlyon.enedis.year'
    )
    await storeData(aggregatedYearData, 'com.grandlyon.enedis.year', ['year'])
Hugo SUBTIL's avatar
Hugo SUBTIL committed
/**
 * @returns {boolean}
 */
function isFirstStart(account) {
  if (account && account.data && account.data.consentId) {
    log('info', 'Konnector not first start')
    return false
  }
  log('info', 'Konnector first start')
  return true
}

Hugo NOUTS's avatar
Hugo NOUTS committed

/***/ }),
/* 1 */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

const requestFactory = __webpack_require__(2);

Hugo SUBTIL's avatar
Hugo SUBTIL committed
const hydrateAndFilter = __webpack_require__(353);
Hugo NOUTS's avatar
Hugo NOUTS committed

const categorization = __webpack_require__(1232);
Hugo NOUTS's avatar
Hugo NOUTS committed

const log = __webpack_require__(1249);
Hugo NOUTS's avatar
Hugo NOUTS committed

module.exports = {
  BaseKonnector: __webpack_require__(1250),
  CookieKonnector: __webpack_require__(1330),
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  cozyClient: __webpack_require__(485),
  errors: __webpack_require__(1256),
Hugo NOUTS's avatar
Hugo NOUTS committed
  log,
  saveFiles: __webpack_require__(1252),
  saveBills: __webpack_require__(1251),
  saveIdentity: __webpack_require__(1296),
  linkBankOperations: __webpack_require__(1273),
  addData: __webpack_require__(1272),
Hugo NOUTS's avatar
Hugo NOUTS committed
  hydrateAndFilter,
  htmlToPDF: (__webpack_require__(1331).htmlToPDF),
  createCozyPDFDocument: (__webpack_require__(1331).createCozyPDFDocument),
Hugo NOUTS's avatar
Hugo NOUTS committed
  filterData: deprecate(hydrateAndFilter, 'Use hydrateAndFilter now. filterData will be removed in cozy-konnector-libs@4'),
  updateOrCreate: __webpack_require__(1295),
Hugo NOUTS's avatar
Hugo NOUTS committed
  request: deprecate(requestFactory, 'Use requestFactory instead of request. It will be removed in cozy-konnector-libs@4'),
  requestFactory,
  retry: __webpack_require__(1253),
  wrapIfSentrySetUp: (__webpack_require__(1297).wrapIfSentrySetUp),
  Document: __webpack_require__(1332),
  signin: __webpack_require__(1292),
  submitForm: __webpack_require__(1292),
  scrape: __webpack_require__(1334),
  mkdirp: __webpack_require__(1255),
  normalizeFilename: __webpack_require__(1335),
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  utils: __webpack_require__(484),
  solveCaptcha: __webpack_require__(1336),
Hugo NOUTS's avatar
Hugo NOUTS committed
  createCategorizer: categorization.createCategorizer,
  categorize: categorization.categorize,
  manifest: __webpack_require__(915)
Hugo NOUTS's avatar
Hugo NOUTS committed
};

function deprecate(wrapped, message) {
  return function () {
    log('warn', message);
    return wrapped.apply(this, arguments);
  };
}

/***/ }),
/* 2 */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

module.exports = __webpack_require__(3)["default"];

/***/ }),
/* 3 */
/***/ ((module, exports, __webpack_require__) => {

/**
 * This is a function which returns an instance of
 * [request-promise](https://www.npmjs.com/package/request-promise) initialized with
 * defaults often used in connector development.
 *
 * ```js
 * // Showing defaults
 * req = requestFactory({
 *   cheerio: false,
 *   jar: true,
 *   json: true
 * })
 * ```
 *
 * Options :
 *
 * - `cheerio`:  will parse automatically the `response.body` in a cheerio instance
 *
 * ```javascript
 * req = requestFactory({ cheerio: true })
 * req('http://github.com', $ => {
 *   const repos = $('#repo_listing .repo')
 * })
 * ```
 *
 * - `jar`: is passed to `request` options. Remembers cookies for future use.
 * - `json`: will parse the `response.body` as JSON
 * - `resolveWithFullResponse`: The full response will be return in the promise. It is compatible
 *   with cheerio and json options.
 *
 * ```javascript
 * req = requestFactory({
 *    resolveWithFullResponse: true,
 *    cheerio: true
 * })
 * req('http://github.com', response => {
 *   console.log(response.statusCode)
 *   const $ = response.body
 *   const repos = $('#repo_listing .repo')
 * })
 * ```
 * - `debug`: will display request and responses details in error output. Possible values :
 *   true : display request and response in normal request-debug json format
 *   'json' : display request and response in full json format
 *   'simple' : display main information about each request and response
 *   ```
 *   GET -> http://books.toscrape.com/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg
 *   <- 200  Content-Length: 7095
 *   ```
 *   'full' : display comple information about each request and response
 *   ```
 *   GET -> http://quotes.toscrape.com/login
 *
 *   BEGIN HEADERS
 *   host: quotes.toscrape.com
 *   END HEADERS
 *
 *   <- 200  Content-Length: 1869
 *
 *   BEGIN HEADERS
 *   server: nginx/1.12.1
 *   ...
 *   END HEADERS
 *
 *   BEGIN BODY
 *   <html>....
 *   END BODY
 *   ```
 *
 * You can find the full list of available options in [request-promise](https://github.com/request/request-promise) and [request](https://github.com/request/request) documentations.
 *
 * @module requestFactory
 */
let request = __webpack_require__(4);

Hugo SUBTIL's avatar
Hugo SUBTIL committed
const requestdebug = __webpack_require__(254); // Quickly found more UserAgent here
Hugo NOUTS's avatar
Hugo NOUTS committed
// https://www.whatismybrowser.com/guides/the-latest-user-agent/


const AGENTS_LIST = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11.1; rv:84.0) Gecko/20100101 Firefox/84.0', 'Mozilla/5.0 (X11; Linux i686; rv:84.0) Gecko/20100101 Firefox/84.0', 'Mozilla/5.0 (Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0', 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:84.0) Gecko/20100101 Firefox/84.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75'];
const DEFAULT_USER_AGENT = AGENTS_LIST[Math.floor(Math.random() * AGENTS_LIST.length)];
exports = module.exports = {
  default: requestFactory,
  mergeDefaultOptions,
  transformWithCheerio,
  getRequestOptions,
  DEFAULT_USER_AGENT
};

function requestFactory({
  debug,
  ...options
} = {
  debug: false
}) {
  const logFn = setDebugFunction(debug);
  debug && requestdebug(request, logFn);
  return request.defaults(getRequestOptions(mergeDefaultOptions(options)));
}

function setDebugFunction(debug) {
  /* eslint no-console: off */
  if (debug === 'simple') {
    return (type, data) => console.error(requestToStrings(type, data).oneline);
  } else if (debug === 'json') {
    return (type, data) => console.error(JSON.stringify({
      type,
      data
    }));
  } else if (debug === 'full') {
    return (type, data) => {
      const {
        oneline,
        headers,
        body
      } = requestToStrings(type, data);
      console.error(`${oneline}

BEGIN HEADERS
${headers}
END HEADERS

` + (body ? `BEGIN BODY
${body}
END BODY

` : ''));
    };
  } else if (typeof debug === 'function') {
    return (type, data, resp) => debug({
      strings: requestToStrings(type, data),
      type,
      data,
      resp
    });
  }
}

function requestToStrings(type, data) {
  const result = {};

  if (type === 'request') {
    result.oneline = `${data.method} -> ${data.uri} ${data.headers['content-length'] ? 'Content-Length: ' + data.headers['content-length'] : ''}`;
  } else if (type === 'response') {
    result.oneline = `<- ${data.statusCode}  ${data.headers['content-length'] ? 'Content-Length: ' + data.headers['content-length'] : ''}`;
  } else {
    result.oneline = `<- ${data.statusCode} ${data.uri}`;
  }

  result.headers = Object.keys(data.headers).map(key => `${key}: ${data.headers[key]}`).join('\n');
  result.body = data.body;
  return result;
}

function mergeDefaultOptions(options = {}) {
  const defaultOptions = {
    debug: false,
    json: true,
    cheerio: false,
    headers: {},
    followAllRedirects: true
  };

  if (options.cheerio === true) {
    options.json = false;
  }

  return { ...defaultOptions,
    ...options
  };
}

function transformWithCheerio(body, response, resolveWithFullResponse) {
Hugo SUBTIL's avatar
Hugo SUBTIL committed
  const result = (__webpack_require__(256).load)(body);
Hugo NOUTS's avatar
Hugo NOUTS committed

  if (resolveWithFullResponse) {
    return { ...response,
      body: result
    };
  }

  return result;
}

function getRequestOptions({
  cheerio,
  userAgent,
  ...options
}) {
  const userAgentOption = options.headers['User-Agent'];
  return cheerio ? { ...options,
    transform: transformWithCheerio,
    headers: { ...options.headers,
      'User-Agent': userAgentOption ? userAgentOption : userAgent === false ? undefined : DEFAULT_USER_AGENT
    }
  } : { ...options,
    headers: { ...options.headers,
      'User-Agent': userAgent ? DEFAULT_USER_AGENT : options.headers['User-Agent']
    }
  };
}

/***/ }),
/* 4 */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

"use strict";
/* module decorator */ module = __webpack_require__.nmd(module);


var Bluebird = (__webpack_require__(5).getNewLibraryCopy)(),
    configure = __webpack_require__(42),
    stealthyRequire = __webpack_require__(57);

try {

    // Load Request freshly - so that users can require an unaltered request instance!
    var request = stealthyRequire(__webpack_require__.c, function () {
        return __webpack_require__(58);
    },
    function () {
        __webpack_require__(61);
    }, module);

} catch (err) {
    /* istanbul ignore next */
Hugo SUBTIL's avatar
Hugo SUBTIL committed
    var EOL = (__webpack_require__(253).EOL);
Hugo NOUTS's avatar
Hugo NOUTS committed
    /* istanbul ignore next */
    console.error(EOL + '###' + EOL + '### The "request" library is not installed automatically anymore.' + EOL + '### But is a dependency of "request-promise".' + EOL + '### Please install it with:' + EOL + '### npm install request --save' + EOL + '###' + EOL);
    /* istanbul ignore next */
    throw err;
}

Bluebird.config({cancellation: true});

configure({
    request: request,
    PromiseImpl: Bluebird,
    expose: [
        'then',
        'catch',
        'finally',
        'cancel',
        'promise'
        // Would you like to expose more Bluebird methods? Try e.g. `rp(...).promise().tap(...)` first. `.promise()` returns the full-fledged Bluebird promise.
    ],
    constructorMixin: function (resolve, reject, onCancel) {
        var self = this;
        onCancel(function () {
            self.abort();
        });
    }
});

request.bindCLS = function RP$bindCLS() {
    throw new Error('CLS support was dropped. To get it back read: https://github.com/request/request-promise/wiki/Getting-Back-Support-for-Continuation-Local-Storage');
};


module.exports = request;


/***/ }),
/* 5 */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

"use strict";

var old;
if (typeof Promise !== "undefined") old = Promise;
function noConflict() {
    try { if (Promise === bluebird) Promise = old; }
    catch (e) {}
    return bluebird;
}
var bluebird = __webpack_require__(6)();
bluebird.noConflict = noConflict;
module.exports = bluebird;


/***/ }),
/* 6 */
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

"use strict";

module.exports = function() {
var makeSelfResolutionError = function () {
    return new TypeError("circular promise resolution chain\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
};
var reflectHandler = function() {
    return new Promise.PromiseInspection(this._target());
};
var apiRejection = function(msg) {
    return Promise.reject(new TypeError(msg));
};
function Proxyable() {}
var UNDEFINED_BINDING = {};
var util = __webpack_require__(7);
util.setReflectHandler(reflectHandler);

var getDomain = function() {
    var domain = process.domain;
    if (domain === undefined) {
        return null;
    }