Home Reference Source

src/webpurify.js

import http from 'http';
import https from 'https';
import url from 'url';

import Configuration, { API_HOSTS } from './configuration';

/**
 * WebPurify NPM Module
 * A Node NPM module for interacting with the WebPurify API
*/
export default class WebPurify {
  /**
   * @param {Object} options - Pass configuration options here, or declare them in their respective ENV variables.
   * @param {Object} options.api_key - WebPurify API Key. ENV variable (takes precedence): WEBPURIFY_API_KEY
   * @param {Object} options.endpoint - Available: 'us', 'eu', 'ap'. Default: 'us'. ENV variable: WEBPURIFY_ENDPOINT
   * @param {Object} options.enterprise - Available: true, false. Default: false. ENV varable: WEBPURIFY_ENTERPRISE
   * @throws {Error} Throws an error if parameters are invalid.
   * @throws {Error} Throws an error if API key is missing.
   * @returns {WebPurify} A WebPurify instance.
   */
  constructor(options) {
    const configuration = new Configuration(options);
    this._config = configuration.config;
    this._request_base = { host: this._config.endpoint, path: configuration.path };
    this._query_base = { api_key: this._config.api_key, format: 'json' };
  }


  /**
   * Handles the HTTP/S requests
   * @param {string} host - The hostname for the request URL (ie. api1.webpurify.com)
   * @param {string} path - The path of the request (ie. /services/rest/)
   * @param {string} method - The method, either 'GET or 'PUT'
   * @param {boolean} ssl - True or false for using HTTPS or HTTP. If you are using enterprise API, you can set this to true.
   * @return {Promise}
   */
  request(host, path, method, ssl) {
    let options = {
      hostname: host,
      path: path,
      method: method
    };
    const baseType = ssl ? http : https;
    return new Promise((resolve, reject) => {
      const req = baseType.request(options, (res) => {
        const buff = [];
        res.on('data', chunk => buff.push(chunk));
        res.on('end', () => {
          try {
            let parsed = JSON.parse(buff.toString());
            return resolve(parsed);
          } catch (error) {
            return reject(error);
          }
        });
      });
      req.on('error', (error) => reject(error));
      req.end();
    });
  }


  /**
   * Formats the request for the request function
   * @param {Object} params - The params object passed into the request
   * @param {Object} [options={}] - The optional parameters for the API request
   * @param {Object} [host=this._request_base.host] - Optional request host
   * @return {Promise}
   */
  async get(params, options = {}, host = this._request_base.host) {
    // form query parameters
    let query = Object.assign(this._query_base, params, options);
    const path = url.format({ pathname: this._request_base.path, query });

    let rsp = null;
    let parsed;

    // make request
    try {
      parsed = await this.request(host, path, 'GET', this._config.enterprise);
      rsp = parsed ? parsed.rsp : null;
    } catch(error) {
      return error;
    }

    if (!rsp || !rsp.hasOwnProperty('@attributes')) {
      const error = new Error("Malformed Webpurify response");
      error.response = parsed;
      return Promise.reject(error);
    }

    if (rsp.hasOwnProperty('err')) {
      const errAttrs = rsp.err['@attributes'] || { msg: "Unknown Webpurify Error" };
      const error = new Error(errAttrs.msg);
      error.code = errAttrs.code;
      return Promise.reject(error);
    }

    return this.strip(rsp);
  }


  /**
   * Strips the WebPurify JSON response to be useful
   * @param {Object} response - The response JSON to be stripped
   * @return {Object} The stripped response
   */
  strip(response) {
    if (response) {
      delete response['@attributes'];
      delete response.api_key;
      delete response.method;
      delete response.format;
    }
    return response;
  }


  /**
   * Checks the passed text for any profanity. If found, returns true, else false.
   * @param {string} text - The text to check for profanity
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.lang] - The 2 letter language code for the text you are submitting
   * @param {Object} [options.semail] - Treat email addresses like profanity. set = 1
   * @param {Object} [options.sphone] - Treat phone numbers like profanity. set = 1
   * @param {Object} [options.slink] - Treat urls like profanity. set = 1
   * @param {Object} [options.rsp] - To include our response time in the result. set = 1
   * @return {Promise}
   */
  async check(text, options) {
    const method = 'webpurify.live.check';
    const params = { method, text };
    try {
      const res = await this.get(params, options);
      return res.found === '1';
    } catch(error) {
      return error;
    }
  }


  /**
   * Checks the passed text for any profanity. If found, returns number of found words, else 0.
   * @param {string} text - The text to check for profanity
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.lang] - The 2 letter language code for the text you are submitting
   * @param {Object} [options.semail] - Treat email addresses like profanity. set = 1
   * @param {Object} [options.sphone] - Treat phone numbers like profanity. set = 1
   * @param {Object} [options.slink] - Treat urls like profanity. set = 1
   * @param {Object} [options.rsp] - To include our response time in the result. set = 1
   * @return {Promise}
   */
  async checkCount(text, options) {
    let method = 'webpurify.live.checkcount';
    let params = { method, text };

    try {
      const res = await this.get(params, options);
      return parseInt(res.found, 10);
    } catch(error) {
      return error;
    }
  }


  /**
   * Checks the passed text for any profanity. If found, returns the text with profanity altered by symbol. Else 0.
   * @param {string} text - The text to check for profanity
   * @param {string} replacesymbol - The symbol to replace profanity with (ie. '*')
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.lang] - The 2 letter language code for the text you are submitting
   * @param {Object} [options.semail] - Treat email addresses like profanity. set = 1
   * @param {Object} [options.sphone] - Treat phone numbers like profanity. set = 1
   * @param {Object} [options.slink] - Treat urls like profanity. set = 1
   * @param {Object} [options.rsp] - To include our response time in the result. set = 1
   * @return {Promise}
   */
  async replace(text, replacesymbol, options) {
    let method = 'webpurify.live.replace';
    let params = { method, text, replacesymbol };
    try {
      const res = await this.get(params, options);
      return res.text;
    } catch(error) {
      return error;
    }
  }


  /**
   * Checks the passed text for any profanity. If found, returns an array of expletives.
   * @param {string} text - The text to check for profanity
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.lang] - The 2 letter language code for the text you are submitting
   * @param {Object} [options.semail] - Treat email addresses like profanity. set = 1
   * @param {Object} [options.sphone] - Treat phone numbers like profanity. set = 1
   * @param {Object} [options.slink] - Treat urls like profanity. set = 1
   * @param {Object} [options.rsp] - To include our response time in the result. set = 1
   * @return {Promise}
   */
  async return(text, options) {
    let method = 'webpurify.live.return';
    let params = { method, text };
    try {
      const res = await this.get(params, options);
      return [].concat(res.expletive).filter(w => typeof w === 'string');
    } catch(error) {
      return error;
    }
  }


  /**
   * Add a word to the blacklist
   * @param {string} word - The word to add to the blacklist
   * @param {string} [ds=0] - 1 if deepsearch, 0 or null if you don't care
   * @return {Promise}
   */
  async addToBlacklist(word, ds = 0) {
    let method = 'webpurify.live.addtoblacklist';
    let params = { method, word, ds };
    try {
      const res = await this.get(params);
      return res.success === '1';
    } catch(error) {
      return error;
    }
  }


  /**
   * Remove a word from the blacklist
   * @param {string} word - The word to remove from the blacklist
   * @return {Promise}
   */
  async removeFromBlacklist(word) {
    let method = 'webpurify.live.removefromblacklist';
    let params = { method, word };
    try {
      const res = await this.get(params);
      return res.success === '1';
    } catch(error) {
      return error;
    }
  }


  /**
   * Get the blacklist
   * @param {string} [ds=0] - Set equal to 1 to show which of the blacklist items have “deep search” turned on.
   * @return {Promise}
   */
  async getBlacklist(ds = 0) {
    let method = 'webpurify.live.getblacklist';
    let params = { method };
    try {
      const res = await this.get(params, options);
      return [].concat(res.word).filter(w => typeof w === 'string');
    } catch(error) {
      return error;
    }
  }


  /**
   * Add a word to the whitelist
   * @param {string} word - The word to add to the whitelist
   * @param {string} [ds=0] - 1 if deepsearch, 0 or null if you don't care
   * @return {Promise}
   */
  async addToWhitelist(word, ds = 0) {
    let method = 'webpurify.live.addtowhitelist';
    let params = { method, word };
    try {
      const res = await this.get(params);
      return res.success === '1';
    } catch(error) {
      return error;
    }
  }


  /**
   * Remove a word from the whitelist
   * @param {string} word - The word to remove from the whitelist
   * @return {Promise}
   */
  async removeFromWhitelist(word) {
    let method = 'webpurify.live.removefromwhitelist';
    let params = { method, word };
    try {
      const res = await this.get(params);
      return res.success === '1';
    } catch(error) {
      return error;
    }
  }


  /**
   * Get the whitelist
   * @param {string} [ds=0] - Set equal to 1 to show which of the blacklist items have “deep search” turned on.
   * @return {Promise}
   */
  async getWhitelist(ds = 0) {
    let method = 'webpurify.live.getwhitelist';
    let params = { method };
    try {
      const res = await this.get(params, options);
      return [].concat(res.word).filter(w => typeof w === 'string');
    } catch(error) {
      return error;
    }
  }

  /**
   * Checks the imgid for status of moderation.
   * @param {string} imgid - The URL of the image
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.customimgid] - A custom ID you wish to associate with the image that will be carried through to the callback
   * @return {Promise}
   */
  async imgStatus(imgid, options = {}) {
    let method = 'webpurify.live.imgstatus';
    let params = { method, imgid };
    try {
      const res = await this.get(params, options, API_HOSTS['im']);
      return res.status;
    } catch(error) {
      return error;
    }
  }

  /**
   * Checks the passed imageurl moderation. It will need a callback.
   * @param {string} imgurl - Full url to the image you would like moderated.
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.customimgid] - A custom ID you wish to associate with the image that will be carried through to the callback
   * @param {Object} [options.callback] - You may also submit a URL encoded callback on a per image basis
   * @param {Object} [options.snstopic] - SNS Topic name. To use AWS SNS to receive results
   * @param {object} [options.photodna] - Set = 1 to check images for matches against known child exploitation hash datasets – You must activate PhotoDNA for your API Key in your admin console.
   * @return {Promise}
   */
  async imgCheck(imgurl, options) {
    let method = 'webpurify.live.imgcheck';
    let params = { method, imgurl };
    try {
      const res = await this.get(params, options, API_HOSTS['im']);
      return res.imgid;
    } catch(error) {
      return error;
    }
  }

  /**
   * Checks the remaining submissions on licence for images.
   * @return {Promise}
   */
  async imgAccount() {
    let method = 'webpurify.live.imgaccount';
    let params = { method };
    try {
      const res = await this.get(params, null, API_HOSTS['im']);
      return res.remaining;
    } catch(error) {
      return error;
    }
  }

  /**
   * Checks the passed imageurl moderation. It will need a callback.
   * @param {string} imgurl - The URL of the image
   * @return {Promise}
   */
  async aimImgCheck(imgurl) {
    let method = 'webpurify.aim.imgcheck';
    let params = { method, imgurl };
    try {
      const res = await this.get(params, null, API_HOSTS['im']);
      return Number.parseFloat(res.nudity);
    } catch(error) {
      return error;
    }
  }

  /**
   * Check the number of AIM image submissions remaining on your license.
   * @return {Promise}
   */
  async aimImgAccount() {
    let method = 'webpurify.aim.imgaccount';
    let params = { method };
    try {
      const res = await this.get(params, null, API_HOSTS['im']);
      return res.remaining;
    } catch(error) {
      return error;
    }
  }

  /**
   * Combine our Automated Intelligent Moderation system (AIM) and our Live
   * moderators to create a powerful low cost solution.
   *
   * Images submitted to this method, are first sent to AIM and then sent to
   * our live moderation team based on thresholds you set.
   *
   * I.E any image that is given a 50% or greater probability by AIM can then be
   * sent to our human moderation team for further review.
   *
   * @param {string} imgurl - The URL of the image
   * @param {Object} [options] - The optional API parameters
   * @param {Object} [options.thresholdlt] - Set the lower threshold to pass the image to our live team. E.g. thresholdlt=50 would send all images that AIM gives a nudity probability of less than 50 to our live team.
   * @param {Object} [options.thresholdgt] - Set the upper threshold to pass the image to our live team. E.g. thresholdgt=70 would send all images that AIM gives a nudity probability of greater than 70 to our live team.
   * @param {Object} [options.customimgid] - A custom ID you wish to associate with the image that will be carried through to the callback
   * @param {Object} [options.callback] - You may also submit a URL encoded callback on a per image basis
   * @return {Promise}
   */
  async hybridImgCheck(imgurl, options) {
    let method = 'webpurify.hybrid.imgcheck';
    let params = { method, imgurl };
    try {
      const res = await this.get(params, options, API_HOSTS['im']);
      return Number.parseFloat(res.nudity);
    } catch(error) {
      return error;
    }
  }
}