Skip to content
Snippets Groups Projects
index.js 8.75 MiB
Newer Older
  • Learn to ignore specific revisions
  •     await this.saveSession();
        return super.end();
      }
      /**
       * Calls cozy-konnector-libs requestFactory forcing this._jar as the cookie
       *
       * @param  {object} options - requestFactory option
       * @returns {object} - The resulting request object
       */
    
      requestFactory(options) {
        this._jar = this._jar || requestFactory().jar();
        return requestFactory({ ...options,
          jar: this._jar
        });
      }
      /**
       * Reset cookie session with a new empty session and save it to the associated account
       *
       * @returns {Promise} empty promise
       */
    
      async resetSession() {
        log('debug', 'Reset cookie session...');
        this._jar = requestFactory().jar();
        return this.saveSession();
      }
      /**
       * Get the cookie session from the account if any
       *
       * @returns {Promise} true or false if the session in the account exists or not
       */
    
      async initSession() {
        const accountData = this.getAccountData();
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        try {
          if (this._account.state === 'RESET_SESSION') {
            log('debug', 'RESET_SESSION state found');
            await this.resetSession();
            await this.updateAccountAttributes({
              state: null
            });
    
    Romain CREY's avatar
    Romain CREY committed
          }
    
        } catch (err) {
          log('warn', 'Could not reset the session');
          log('warn', err.message);
        }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          if (accountData && accountData.auth) {
            jar = JSON.parse(accountData.auth[JAR_ACCOUNT_KEY]);
    
    Romain CREY's avatar
    Romain CREY committed
          }
    
    
          if (jar) {
            log('debug', 'found saved session, using it...');
            this._jar._jar = CookieJar.fromJSON(jar, this._jar._jar.store);
            return true;
    
    Romain CREY's avatar
    Romain CREY committed
          }
    
        } catch (err) {
          log('debug', 'Could not parse session');
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        log('debug', 'Found no session');
        return false;
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
      /**
       * Saves the current cookie session to the account
       *
       * @returns {Promise} empty promise
       */
    
      async saveSession(obj) {
        const accountData = { ...this._account.data,
          auth: {}
        };
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        if (obj && obj.getCookieJar) {
          this._jar._jar = obj.getCookieJar();
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        accountData.auth[JAR_ACCOUNT_KEY] = JSON.stringify(this._jar._jar.toJSON());
        await this.saveAccountData(accountData);
        log('debug', 'saved the session');
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
      /**
       * This is signin function from cozy-konnector-libs which is forced to use the current cookies
       * and current request from CookieKonnector. It also automatically saves the session after
       * signin if it is a success.
       *
       * @returns {Promise} resolve with an object containing form data
       */
    
      async signin(options) {
        const result = await super.signin({ ...options,
          requestInstance: this.request
        });
        await this.saveSession();
        return result;
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
      /**
       * This is saveFiles function from cozy-konnector-libs which is forced to use the current cookies
       * and current request from CookieKonnector.
       *
       * @returns {Promise} resolves with the list of entries with file objects
       */
    
      saveFiles(entries, fields, options) {
        return super.saveFiles(entries, fields, { ...options,
          requestInstance: this.request
        });
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
      /**
       * This is saveBills function from cozy-konnector-libs which is forced to use the current cookies
       * and current request from CookieKonnector.
       *
       * @returns {Promise} resolves with entries hydrated with db data
       */
    
      saveBills(entries, fields, options) {
        return super.saveBills(entries, fields, { ...options,
          requestInstance: this.request
        });
    
    module.exports = CookieKonnector;
    
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports, __webpack_require__) {
    
    
    const {
      URL
    } = __webpack_require__(82);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const computeWidth = $table => {
      let out = 0;
      const tds = $table.find('tr').first().find('td,th');
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      for (var i = 0; i < tds.length; i++) {
        out += parseInt(tds.eq(i).attr('colspan')) || 1;
      }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const makeLinkOpts = ($el, opts) => {
      if ($el.attr('href') === undefined) return undefined;
      if ($el.attr('href').indexOf('javascript:') === 0) return undefined;
      if ($el.attr('href').indexOf('#') === 0) return undefined;
      return {
        link: new URL($el.attr('href'), opts.baseURL).toString(),
        color: '0x0000FF'
      };
    };
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    function htmlToPDF($, frag, $parent, opts) {
      let pdf, helveticaBold, helveticaEm; // pdfjs is an optional dependency, this is why the requires are done in
      // a try/catch. webpack detects this and does not crash when building
      // if requires are done in a try/catch.
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      try {
        pdf = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'pdfjs'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()));
        helveticaBold = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'pdfjs/font/Helvetica-Bold'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()));
        helveticaEm = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'pdfjs/font/Helvetica-Oblique'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()));
      } catch (err) {
        throw new Error('pdfjs dependency is missing. Please add it in your package.json');
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    
      opts = Object.assign({
        baseURL: '',
        filter: () => true,
        txtOpts: {}
      }, opts);
      const children = $parent.contents();
      let text = opts.text;
      let parentDL = null;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      const getText = () => {
        if (!text) text = frag.text('', opts.txtOpts);
        return text;
      };
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      children.each((i, el) => {
        if (el.nodeType === 3 && el.data.trim() !== '') {
          getText().add(el.data);
        } else if (el.nodeType === 1) {
          const $el = $(el);
          if (!opts.filter($el)) return;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          switch (el.tagName) {
            case 'a':
              getText().add($el.text(), makeLinkOpts($el, opts));
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'strong':
            case 'b':
              getText().add($el.text(), {
                font: helveticaBold
              });
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'em':
              getText().add($el.text(), {
                font: helveticaEm
              });
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'span':
              htmlToPDF($, frag, $el, Object.assign({}, opts, {
                text: text
              }));
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'br':
              getText().br();
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'i':
            case 'select':
            case 'input':
            case 'label':
            case 'form':
            case 'fieldset':
            case 'textarea':
            case 'button':
            case 'img':
            case 'script':
            case 'caption':
              // ignore
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'table':
              {
                text = null;
                let width = computeWidth($el);
                let tableState = {
                  tableWidth: width
                };
                htmlToPDF($, frag.table({
                  widths: Array.from(Array(width), () => '*'),
                  borderWidth: 1
                }), $el, Object.assign({}, opts, {
                  tableState
                }));
                break;
              }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'tr':
              {
                text = null;
                opts.tableState.colRemaining = opts.tableState.tableWidth;
                let row = frag.row();
                htmlToPDF($, row, $el, opts);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
                if (opts.tableState.colRemaining > 0) {
                  row.cell({
                    colspan: opts.tableState.colRemaining
                  });
                }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'dl':
              text = null;
              htmlToPDF($, frag.table({
                widths: [5 * pdf.cm, null],
                borderWidth: 1
              }), $el, { ...opts,
                tableState: {
                  tableWidth: 2,
                  colRemaining: 2
                }
              });
              parentDL = null;
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'dt':
              if (!parentDL) {
                parentDL = frag;
              } else {
                frag = parentDL;
              }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
              frag = frag.row();
            // fall through the rest of the procedure
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'dd':
            case 'th':
            case 'td':
              {
                text = null;
                const colspan = Math.min(opts.tableState.tableWidth, parseInt($el.attr('colspan')) || 1);
                opts.tableState.colRemaining -= colspan;
                htmlToPDF($, frag.cell({
                  padding: 5,
                  colspan: colspan
                }), $el, opts);
                break;
              }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'h1':
            case 'h2':
            case 'h3':
            case 'h4':
            case 'h5':
              text = null;
              htmlToPDF($, frag, //.cell({ paddingTop: 1 * pdf.cm }),
              $el, Object.assign({}, opts, {
                txtOpts: {
                  fontSize: 30 - parseInt(el.tagName.replace('h', '')) * 2
                }
              }));
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'div':
            case 'p':
            case 'ul':
              text = null;
              htmlToPDF($, frag.cell(), $el, opts);
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            case 'thead':
            case 'tfoot':
            case 'tbody':
            case 'small':
            case 'li':
              text = null;
              htmlToPDF($, frag, $el, opts);
              break;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            default:
              text = null;
              htmlToPDF($, frag, $el, opts);
    
    Romain CREY's avatar
    Romain CREY committed
          }
        }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    function createCozyPDFDocument(headline, url) {
      let pdf, helveticaBold; // pdfjs is an optional dependency, this is why the requires are done in
      // a try/catch. webpack detects this and does not crash when building
      // if requires are done in a try/catch.
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      try {
        pdf = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'pdfjs'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()));
        helveticaBold = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'pdfjs/font/Helvetica-Bold'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()));
      } catch (err) {
        throw new Error('pdfjs dependency is missing. Please add it in your package.json');
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    
      var doc = new pdf.Document();
      const cell = doc.cell({
        paddingBottom: 0.5 * pdf.cm
      }).text();
      cell.add(headline, {
        font: helveticaBold,
        fontSize: 14
      });
      cell.add(url, {
        link: url,
        color: '0x0000FF'
      });
      return doc;
    
    Romain CREY's avatar
    Romain CREY committed
    }
    
    
    module.exports = {
      htmlToPDF,
      createCozyPDFDocument
    };
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /***/ }),
    /* 1731 */
    /***/ (function(module, exports, __webpack_require__) {
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const isEqualWith = __webpack_require__(1732);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const omit = __webpack_require__(902);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const maybeToISO = date => {
      try {
        return date.toISOString ? date.toISOString() : date;
      } catch (e) {
        return date;
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const looseDates = (val, otherVal) => {
      // Loose equality for dates since when coming from Couch, they
      // are ISO strings whereas just after scraping they are `Date`s.
      if (val instanceof Date) {
        return maybeToISO(val) === maybeToISO(otherVal);
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    };
    /**
     * Simple Model for Documents. Allows to specify
     * `shouldSave`, `shouldUpdate` as methods.
     *
     * Has useful `isEqual` method
     *
     */
    
    class Document {
      constructor(attrs) {
        if (this.validate) {
          this.validate(attrs);
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        Object.assign(this, attrs, {
          metadata: {
            version: attrs.metadata && attrs.metadata.version || this.constructor.version
    
    Romain CREY's avatar
    Romain CREY committed
          }
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
      /**
       * Compares to another document deeply.
       *
       * `_id` and `_rev` are by default ignored in the comparison.
       *
       * By default, will compare dates loosely since you often
       * compare existing documents (dates in ISO string) with documents
       * that just have been scraped where dates are `Date`s.
       */
    
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      isEqual(other, ignoreAttrs = ['_id', '_rev'], strict = false) {
        return isEqualWith(omit(this, ignoreAttrs), omit(other, ignoreAttrs), !strict && looseDates);
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    Romain CREY's avatar
    Romain CREY committed
    }
    
    
    module.exports = Document;
    
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports, __webpack_require__) {
    
    
    var baseIsEqual = __webpack_require__(476);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /**
     * This method is like `_.isEqual` except that it accepts `customizer` which
     * is invoked to compare values. If `customizer` returns `undefined`, comparisons
     * are handled by the method instead. The `customizer` is invoked with up to
     * six arguments: (objValue, othValue [, index|key, object, other, stack]).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @param {Function} [customizer] The function to customize comparisons.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     * @example
     *
     * function isGreeting(value) {
     *   return /^h(?:i|ello)$/.test(value);
     * }
     *
     * function customizer(objValue, othValue) {
     *   if (isGreeting(objValue) && isGreeting(othValue)) {
     *     return true;
     *   }
     * }
     *
     * var array = ['hello', 'goodbye'];
     * var other = ['hi', 'goodbye'];
     *
     * _.isEqualWith(array, other, customizer);
     * // => true
     */
    function isEqualWith(value, other, customizer) {
      customizer = typeof customizer == 'function' ? customizer : undefined;
      var result = customizer ? customizer(value, other) : undefined;
      return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result;
    }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    module.exports = isEqualWith;
    
    /***/ }),
    /* 1733 */
    /***/ (function(module, exports, __webpack_require__) {
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const log = __webpack_require__(2).namespace('scrape');
    /**
     * Declarative scraping.
     *
     * Describe your items attributes and where to find/parse them
     * instead of imperatively building them.
     *
     * Heavily inspired by [artoo] scraping method.
     *
     * [artoo]: https://medialab.github.io/artoo/
     */
    
    const mkSpec = function (spec) {
      if (typeof spec === 'string') {
        return {
          sel: spec
        };
      } else {
        return spec;
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    };
    /**
     * Scrape a cheerio object for properties
     *
     * @param  {object} $ - Cheerio node which will be scraped
     * @param  {object|string} specs - Options object describing what you want to scrape
     * @param  {string} [childSelector] -  If passed, scrape will return an array of items
     * @returns {object|Array} - Item(s) scraped
     * @example
     *
     * `scrape` can be used to declaratively extract data :
     *
     * - For one object :
     *
     * ```
     * const item = scrape($('#item'), {
     *   title: '.title',
     *   content: '.content'
     * })
     * ```
     *
     * - For a list of objects :
     *
     * ```
     * const items = scrape($('#content'), {
     *   title: '.title',
     *   content: '.content'
     * }, '.item')
     * ```
     *
     * For more power, you can use `object`s for each retriever :
     *
     * ```
     * const items = scrape($('#content'), {
     *   title: '.title',
     *   content: '.content',
     *   link: {
     *     sel: 'a',
     *     attr: 'href'
     *   },
     * }, '.item')
     * ```
     *
     * Here the `href` attribute of the `a` inside `.item`s would have been
     * put into the `link` attribute of the items returned by `scrape`.
     *
     * Available options :
     *
     * - `sel`: the CSS selector used to target the HTML node from which data will be scraped
     * - `attr`: the HTML attribute from which to extract data
     * - `parse`: function applied to the value extracted (`{ sel: '.price', parse: parseAmount }`)
     * - `fn`: if you need something more complicated than `attr`, you can use this function, it receives
     * the complete DOM node. `{ sel: '.person', fn: $node => $node.attr('data-name') + $node.attr('data-firstname') }`
     */
    
    const scrape = ($, specs, childSelector) => {
      // Only one value shorthand
      if (typeof specs === 'string' || specs.sel && typeof specs.sel === 'string') {
        const {
          val
        } = scrape($, {
          val: specs
        });
        return val;
      } // Several items shorthand
    
      if (childSelector !== undefined) {
        return Array.from(($.find || $)(childSelector)).map(e => scrape($(e), specs));
      } // Several properties "normal" case
    
      const res = {};
      Object.keys(specs).forEach(specName => {
        try {
          const spec = mkSpec(specs[specName]);
          let data = spec.sel ? $.find(spec.sel) : $;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          if (spec.index) {
            data = data.get(spec.index);
          }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          if (spec.fn) {
            val = spec.fn(data);
          } else if (spec.attr) {
            val = data.attr(spec.attr);
          } else {
            val = data;
            val = val && val.text();
            val = val && val.trim();
          }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          if (spec.parse) {
            val = spec.parse(val);
          }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          res[specName] = val;
        } catch (e) {
          log('warn', 'Could not parse for', specName);
          log('warn', e);
        }
      });
      return res;
    };
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    module.exports = scrape;
    
    Romain CREY's avatar
    Romain CREY committed
    
    /***/ }),
    
    /* 1734 */
    /***/ (function(module, exports) {
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /**
     * Returns the given name, replacing characters that could be an issue when
     * used in a filename with spaces.
     *
     * @module normalizeFilename
     */
    const normalizableCharsRegExp = /[<>:"/\\|?*\0\s]+/g;
    /**
     * Returns the given name, replacing characters that could be an issue when
     * used in a filename with spaces.
     *
     * Replaced characters include:
     *
     * - Those forbidden on one or many popular OS or filesystem: `<>:"/\|?*`
     * - Those forbidden by the cozy-stack `\0`, `\r` and `\n`
     * - Multiple spaces and/or tabs are replaced with a single space
     * - Leading & trailing spaces and/or tabs are removed
     *
     * An exception will be thrown in case there is not any filename-compatible
     * character in the given name.
     *
     * Parameters:
     *
     * - `basename` is whatever string you want to generate the filename from
     * - `ext` is an optional file extension, with or without leading dot
     *
     * ```javascript
     * const { normalizeFilename } = require('cozy-konnector-libs')
     *
     * const filename = normalizeFilename('*foo/bar: <baz> \\"qux"\t???', '.txt')
     * // `filename` === `foo bar baz qux.txt`
     * ```
     *
     * @alias module:normalizeFilename
     */
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const normalizeFilename = (basename, ext) => {
      const filename = basename.replace(normalizableCharsRegExp, ' ').trim();
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      if (filename === '') {
        throw new Error('Cannot find any filename-compatible character in ' + JSON.stringify(filename) + '!');
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    
      if (ext == null) ext = '';else if (!ext.startsWith('.')) ext = '.' + ext;
      return filename + ext;
    };
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    module.exports = normalizeFilename;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /***/ }),
    /* 1735 */
    /***/ (function(module, exports, __webpack_require__) {
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /**
     * Use every possible means to solve a captcha. At the moment, Anticaptcha web service is used if
     * any related secret key is found in COZY_PARAMETERS environment variable.
     *
     * @module solveCaptcha
     */
    const log = __webpack_require__(2).namespace('solveCaptcha');
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const errors = __webpack_require__(1654);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const request = __webpack_require__(24);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const sleep = __webpack_require__(9).promisify(global.setTimeout);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const connectorStartTime = Date.now();
    const ms = 1;
    const s = 1000 * ms;
    const m = 60 * s;
    const DEFAULT_TIMEOUT = connectorStartTime + 3 * m; // 3 minutes by default to let 1 min to the connector to fetch files
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /**
     * Use every possible means to solve a captcha. At the moment, Anticaptcha web service is used if
     * any related secret key is found in COZY_PARAMETERS environment variable.
     * If you do not want to solve the captcha each time the connector is run, please also use
     * CookieKonnector which will help you save the session.
     *
     * Parameters:
     *
     * - `params` is an array of objects with any attributes with some mandatory attributes :
     *   + `type` (String): (default recaptcha) type of captcha to solve. can be "recaptcha" or "image" at the moment
     *   + `timeout` (Number): (default 3 minutes after now) time when the solver should stop trying to
     *   solve the captcha
     *   + `websiteKey` (String): the key you can find on the targeted website (for recaptcha)
     *   + `websiteURL` (String): The URL of the page showing the captcha (for recaptcha)
     *   + `body` (String): The base64 encoded image (for image captcha)
     * Returns: Promise with the solved captcha response as a string
     *
     * @example
     *
     * ```javascript
     * const { solveCaptcha } = require('cozy-konnector-libs')
     *
     * const solvedKey = await solveCaptcha({
     *   websiteKey: 'the key in the webpage',
     *   websiteURL: 'http://quotes.toscrape.com/login',
     * })
     * // now use the solveKey to submit your form
     * ```
     *
     * @alias module:solveCaptcha
     */
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    const solveCaptcha = async (params = {}) => {
      const defaultParams = {
        type: 'recaptcha',
        timeout: DEFAULT_TIMEOUT
      };
      params = { ...defaultParams,
        ...params
      };
      const secrets = JSON.parse(process.env.COZY_PARAMETERS || '{}').secret;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      if (params.type === 'recaptcha') {
        checkMandatoryParams(params, ['websiteKey', 'websiteURL']);
        const {
          websiteKey,
          websiteURL
        } = params;
        return solveWithAntiCaptcha({
          websiteKey,
          websiteURL,
          type: 'NoCaptchaTaskProxyless'
        }, params.timeout, secrets, 'gRecaptchaResponse');
      } else if (params.type === 'recaptchav3') {
        checkMandatoryParams(params, ['websiteKey', 'websiteURL', 'pageAction', 'minScore']);
        const {
          websiteKey,
          websiteURL,
          pageAction,
          minScore
        } = params;
        return solveWithAntiCaptcha({
          websiteKey,
          websiteURL,
          pageAction,
          minScore,
          type: 'RecaptchaV3TaskProxyless'
        }, params.timeout, secrets, 'gRecaptchaResponse');
      } else if (params.type === 'image') {
        checkMandatoryParams(params, ['body']);
        return solveWithAntiCaptcha({
          body: params.body,
          type: 'ImageToTextTask'
        }, params.timeout, secrets, 'text');
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    function checkMandatoryParams(params = {}, mandatoryParams = []) {
      const keys = Object.keys(params);
      const missingKeys = mandatoryParams.filter(key => !keys.includes(key));
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      if (missingKeys.length) {
        throw new Error(`${missingKeys.join(', ')} are mandatory to solve the captcha`);
    
    Romain CREY's avatar
    Romain CREY committed
      }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    async function solveWithAntiCaptcha(taskParams, timeout = DEFAULT_TIMEOUT, secrets, resultAttribute = 'gRecaptchaResponse') {
      const antiCaptchaApiUrl = 'https://api.anti-captcha.com';
      let gRecaptchaResponse = null;
      const startTime = Date.now(); // we try to solve the captcha with anticaptcha
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      const clientKey = secrets.antiCaptchaClientKey;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      if (clientKey) {
        log('debug', '  Creating captcha resolution task...');
        const task = await request.post(`${antiCaptchaApiUrl}/createTask`, {
          body: {
            clientKey,
            task: taskParams
          },
          json: true
        });
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        if (task && task.taskId) {
          log('debug', `    Task id : ${task.taskId}`);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
          while (!gRecaptchaResponse) {
            const resp = await request.post(`${antiCaptchaApiUrl}/getTaskResult`, {
              body: {
                clientKey,
                taskId: task.taskId
              },
              json: true
            });
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            if (resp.status === 'ready') {
              if (resp.errorId) {
                log('error', `Anticaptcha error: ${JSON.stringify(resp)}`);
                throw new Error(errors.CAPTCHA_RESOLUTION_FAILED);
    
    Romain CREY's avatar
    Romain CREY committed
              }
    
    
              log('info', `  Found Recaptcha response : ${JSON.stringify(resp)}`);
              return resp.solution[resultAttribute];
            } else {
              log('debug', `    ${Math.round((Date.now() - startTime) / 1000)}s...`);
    
    Romain CREY's avatar
    Romain CREY committed
    
    
              if (Date.now() > timeout) {
                log('warn', `  Captcha resolution timeout`);
                throw new Error(errors.CAPTCHA_RESOLUTION_FAILED + '.TIMEOUT');
    
    Romain CREY's avatar
    Romain CREY committed
              }
    
    
              await sleep(10000);
    
    Romain CREY's avatar
    Romain CREY committed
            }
          }
    
        } else {
          log('warn', 'Could not create anticaptcha task');
          log('warn', JSON.stringify(task));
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
      } else {
        log('warn', 'Could not find any anticaptcha secret key');
      }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
      throw new Error(errors.CAPTCHA_RESOLUTION_FAILED);
    }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    module.exports = solveCaptcha;
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /***/ }),
    /* 1736 */
    /***/ (function(module, exports, __webpack_require__) {
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    /* WEBPACK VAR INJECTION */(function(module) {var require;//! moment.js
    //! version : 2.29.1
    //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
    //! license : MIT
    //! momentjs.com
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    ;(function (global, factory) {
         true ? module.exports = factory() :
        undefined
    }(this, (function () { 'use strict';
    
    Romain CREY's avatar
    Romain CREY committed
    
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        function hooks() {
            return hookCallback.apply(null, arguments);
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        // This is done to register the method called with moment()
        // without creating circular dependencies.
        function setHookCallback(callback) {
            hookCallback = callback;
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function isArray(input) {
            return (
                input instanceof Array ||
                Object.prototype.toString.call(input) === '[object Array]'
            );
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function isObject(input) {
            // IE8 will treat undefined and null as object if it wasn't for
            // input != null
            return (
                input != null &&
                Object.prototype.toString.call(input) === '[object Object]'
            );
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function hasOwnProp(a, b) {
            return Object.prototype.hasOwnProperty.call(a, b);
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function isObjectEmpty(obj) {
            if (Object.getOwnPropertyNames) {
                return Object.getOwnPropertyNames(obj).length === 0;
            } else {
                var k;
                for (k in obj) {
                    if (hasOwnProp(obj, k)) {
                        return false;
                    }
                }
                return true;
    
    Romain CREY's avatar
    Romain CREY committed
            }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        function isUndefined(input) {
            return input === void 0;
        }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        function isNumber(input) {
            return (
                typeof input === 'number' ||
                Object.prototype.toString.call(input) === '[object Number]'
            );
        }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
        function isDate(input) {
            return (
                input instanceof Date ||
                Object.prototype.toString.call(input) === '[object Date]'
            );
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function map(arr, fn) {
            var res = [],
                i;
            for (i = 0; i < arr.length; ++i) {
                res.push(fn(arr[i], i));
            }
            return res;
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function extend(a, b) {
            for (var i in b) {
                if (hasOwnProp(b, i)) {
                    a[i] = b[i];
                }
            }
    
    Romain CREY's avatar
    Romain CREY committed
    
    
            if (hasOwnProp(b, 'toString')) {
                a.toString = b.toString;
    
    Romain CREY's avatar
    Romain CREY committed
            }
    
    
            if (hasOwnProp(b, 'valueOf')) {
                a.valueOf = b.valueOf;
    
    Romain CREY's avatar
    Romain CREY committed
            }
    
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function createUTC(input, format, locale, strict) {
            return createLocalOrUTC(input, format, locale, strict, true).utc();
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function defaultParsingFlags() {
            // We need to deep clone this object.
            return {
                empty: false,
                unusedTokens: [],
                unusedInput: [],
                overflow: -2,
                charsLeftOver: 0,
                nullInput: false,
                invalidEra: null,
                invalidMonth: null,
                invalidFormat: false,
                userInvalidated: false,
                iso: false,
                parsedDateParts: [],
                era: null,
                meridiem: null,
                rfc2822: false,
                weekdayMismatch: false,
            };
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        function getParsingFlags(m) {
            if (m._pf == null) {
                m._pf = defaultParsingFlags();
            }
            return m._pf;
    
    Romain CREY's avatar
    Romain CREY committed
        }
    
    
        var some;
        if (Array.prototype.some) {
            some = Array.prototype.some;
        } else {
            some = function (fun) {