<commit_before>/*
 *   Copyright 2016, <NAME> (<EMAIL>)
 *   Copyright 2015, <NAME> (<EMAIL>)
 *
 *   This file is part of Sentinl (http://github.com/sirensolutions/sentinl)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fs from 'fs';
import querystring from 'querystring';
import _ from 'lodash';
import { assign, pick, isObject } from 'lodash';
import url from 'url';
import Promise from 'bluebird';
import moment from 'moment';
import rison from 'rison';
import mustache from 'mustache';
import { WebClient } from '@slack/client';
import getConfiguration from '../get_configuration';
import getElasticsearchClient from '../get_elasticsearch_client';
import logHistory from '../log_history';
import EmailClient from './email_client';
import Log from '../log';

// actions
import reportAction from './report';

const toString = function (message) {
  if (typeof message !== 'string') {
    return JSON.stringify(message);
  }
  return message;
};

/**
* Perform actions
*
* @param {object} server hapi of Kibana
* @param {object} actions of watcher
* @param {payload} payload of watcher
* @param {task} task of watcher
*/
export default function (server, actions, payload, task) {
  const config = getConfiguration(server);
  let log = new Log(config.app_name, server, 'do_action');
  const client = getElasticsearchClient({server, config});

  /* Email Settings */
  let email;
  try {
    email = new EmailClient(config.settings.email);
  } catch (err) {
    log.error('email client: ' + err.toString());
    logHistory({
      server,
      watcherTitle: task._source.title,
      message: 'email client: ' + err.toString(),
      level: 'high',
      isError: true,
    });
  }

  /* Slack Settings */
  let slack;
  try {
    if (config.settings.slack.active) {
      slack = new WebClient(config.settings.slack.token);
    }
  } catch (err) {
    log.error('slack client: ' + err.toString());
    logHistory({
      server,
      watcherTitle: task._source.title,
      message: 'slack client: ' + err.toString(),
      level: 'high',
      isError: true,
    });
  }

  /* Debounce Function, returns true if throttled */
  var getDuration = require('sum-time');
  var debounce = function (id, period) {
    var duration = getDuration(period);

    var isInThrottlePeriod = function (id, now, duration) {
      return (now - server.sentinlStore.actions[id]) < duration;
    };

    if (duration) {
      var justNow = new Date().getTime();

      if (!_.has(server.sentinlStore, 'actions')) {
        server.sentinlStore.actions = {};
      }

      if (!_.has(server.sentinlStore.actions, id) || !isInThrottlePeriod(id, justNow, duration)) {
        server.sentinlStore.actions[id] = justNow;
        return false;
      }

      // throttle action
      return true;
    } else {
      return false;
    }
  };

  if (task._source.dashboard_link) {
    task._source.dashboard_link = _updateDashboardLinkTimeRange(task._source.dashboard_link, moment().subtract(1, 'hour'), moment());
  }

  /* Loop Actions */
  _.forEach(actions, function (action, actionId) {
    log = new Log(config.app_name, server, task._id);
    log.debug('processing action');

    const actionName = action.name || actionId; // advanced watcher can be without name property

    /* ***************************************************************************** */
    /*
    *   "console" : {
    *      "priority" : "low",
    *      "message" : "Average {{payload.aggregations.avg.value}}",
    *    }
    */

    var priority;
    var formatterConsole;
    var message;
    if (action.console) {
      (async () => {
        try {
          priority = action.console.priority || 'medium';
          formatterConsole = action.console.message ? action.console.message : '{{ payload }}';
          message = mustache.render(formatterConsole, {payload: payload,});
          log.debug('console payload', payload);

          await logHistory({
            server,
            watcherTitle: task._source.title,
            actionName,
            message: toString(message),
            level: priority,
            payload: !task._source.save_payload ? {} : payload,
          });
        } catch (err) {
          log.error('console action: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'console action: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    /* Throttle Action based on 'throttle_period' optional parameter
    /* "throttle_period": "5m"
    /*
    /* ***************************************************************************** */
    if (action.throttle_period) {
      (async () => {
        try {
          const id = `${task._id}_${actionId}`;
          if (debounce(id, action.throttle_period)) {
            log.info(`action throttled, watcher id: ${task._id}, action name: ${actionId}`);
            await logHistory({
              server,
              watcherTitle: task._source.title,
              actionName,
              message: `Action Throttled for ${action.throttle_period}`,
              level: priority,
              payload: {}
            });
            return;
          }
        } catch (err) {
          log.error('throttle: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'throttle: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    *   "email" : {
    *      "to" : "<EMAIL>",
    *      "from" : "<EMAIL>",
    *      "subject" : "Alarm Title",
    *      "priority" : "high",
    *      "body" : "Series Alarm {{ payload._id}}: {{payload.hits.total}}",
    *      "stateless" : false,
    *     }
    */

    var formatterSubject;
    var formatterBody;
    var subject;
    var text;
    if (action.email) {
      (async () => {
        try {
          formatterSubject = action.email.subject || ('SENTINL: ' + actionId);

          formatterBody = action.email.body;
          if (!formatterBody) {
            if (payload.docs) {
              formatterBody = 'Number of documents: {{payload.docs.length}}';
            } else if (payload.sheet) {
              formatterBody = 'Number of 0 sheet data: {{payload.sheet[0].list[0].data.length}}';
            } else { // hits
              formatterBody = 'Series Alarm {{payload._id}}: {{payload.hits.total}}';
            }
          }

          subject = mustache.render(formatterSubject, {payload: payload, watcher: task._source});
          text = mustache.render(formatterBody, {payload: payload, watcher: task._source});
          priority = action.email.priority || 'medium';


          if (!action.email.stateless) {
            // Log Event
            await logHistory({
              server,
              watcherTitle: task._source.title,
              actionName,
              message: toString(text),
              level: priority,
              payload: !task._source.save_payload ? {} : payload,
            });
          }

          log.info('sending email');

          if (!config.settings.email.active) {
            throw new Error('email delivery disabled');
          } else {
            await email.send({
              text,
              from: action.email.from,
              to: action.email.to,
              subject
            });
          }
        } catch (err) {
          log.error('email action: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'email action: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }


    /* ***************************************************************************** */
    /*
    *   "email_html" : {
    *      "to" : "<EMAIL>",
    *      "from" : "<EMAIL>",
    *      "subject" : "Alarm Title",
    *      "priority" : "high",
    *      "body" : "Series Alarm {{ payload._id}}: {{payload.hits.total}}",
    *      "html" : "<p>Series Alarm {{ payload._id}}: {{payload.hits.total}}</p>",
    *      "stateless" : false,
    *     }
    */
    var html;
    if (action.email_html) {
      (async () => {
        try {
          formatterSubject = action.email_html.subject ? action.email_html.subject : 'SENTINL: ' + actionId;

          formatterBody = action.email_html.body;
          if (!formatterBody) {
            if (payload.docs) {
              formatterBody = 'Number of documents: {{payload.docs.length}}';
            } else if (payload.sheet) {
              formatterBody = 'Number of 0 sheet data: {{payload.sheet[0].list[0].data.length}}';
            } else { // hits
              formatterBody = 'Series Alarm {{payload._id}}: {{payload.hits.total}}';
            }
          }

          let formatterConsole = action.email_html.html || '<p>Series Alarm {{ payload._id}}: {{payload.hits.total}}</p>';
          subject = mustache.render(formatterSubject, {payload: payload, watcher: task._source});
          text = mustache.render(formatterBody, {payload: payload, watcher: task._source});
          html = mustache.render(formatterConsole, {payload: payload, watcher: task._source});
          priority = action.email_html.priority || 'medium';

          if (!action.email_html.stateless) {
            // Log Event
            await logHistory({
              server,
              watcherTitle: task._source.title,
              actionName,
              message: toString(text),
              level: priority,
              payload: !task._source.save_payload ? {} : payload,
            });
          }

          log.info('processing html email');

          if (!config.settings.email.active) {
            throw new Error('email delivery disabled');
          } else {
            await email.send({
              text,
              from: action.email_html.from,
              to: action.email_html.to,
              subject,
              attachment: [
                {
                  data: html,
                  alternative: true
                }
              ]
            });
          }
        } catch (err) {
          log.error('html email action: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'html email action: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    *   "report" : {
    *      "to" : "root@localhost",
    *      "from" : "sentinl@localhost",
    *      "subject" : "Report Title",
    *      "priority" : "high",
    *      "body" : "Series Report {{ payload._id}}: {{payload.hits.total}}",
    *      "snapshot" : {
    *         "res" : "1280,900",
    *         "url" : "http://127.0.0.1/app/kibana#/dashboard/Alerts",
    *         "path" : "/tmp/",
    *         "params" : {
    *             "username" : "username",
    *             "password" : "password",
    *             "delay" : 15,
    *             "crop" : false
    *         }
    *       },
    *      "stateless" : false
    *    }
    */
    if (action.report) {
      (async () => {
        try {
          if (!config.settings.report.active) {
            throw new Error('Reports disabled');
          }

          await reportAction({
            server,
            action,
            watcherTitle: task._source.title,
            esPayload: payload,
            actionName,
            emailClient: email,
          });
        } catch (err) {
          log.error(`${task._source.title}, report action: ` + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'report action: ' + err.toString(),
            level: 'high',
            isError: true,
            isReport: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    *   "slack" : {
    *      "channel": '#<channel>',
    *      "message" : "Series Alarm {{ payload._id}}: {{payload.hits.total}}",
    *      "stateless" : false,
    *    }
    */

    if (action.slack) {
      (async () => {
        try {
          let formatter;
          formatter = action.slack.message ? action.slack.message : 'Series Alarm {{ payload._id}}: {{payload.hits.total}}';
          message = mustache.render(formatter, {payload: payload, watcher: task._source});
          priority = action.slack.priority || 'medium';<commit_msg>Moved to correct scope in do_actions<commit_after>/*
 *   Copyright 2016, <NAME> (<EMAIL>)
 *   Copyright 2015, <NAME> (<EMAIL>)
 *
 *   This file is part of Sentinl (http://github.com/sirensolutions/sentinl)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fs from 'fs';
import querystring from 'querystring';
import _ from 'lodash';
import { assign, pick, isObject } from 'lodash';
import url from 'url';
import Promise from 'bluebird';
import moment from 'moment';
import rison from 'rison';
import mustache from 'mustache';
import { WebClient } from '@slack/client';
import getConfiguration from '../get_configuration';
import getElasticsearchClient from '../get_elasticsearch_client';
import logHistory from '../log_history';
import EmailClient from './email_client';
import Log from '../log';

// actions
import reportAction from './report';

const toString = function (message) {
  if (typeof message !== 'string') {
    return JSON.stringify(message);
  }
  return message;
};

/**
* Perform actions
*
* @param {object} server hapi of Kibana
* @param {object} actions of watcher
* @param {payload} payload of watcher
* @param {task} task of watcher
*/
export default function (server, actions, payload, task) {
  const config = getConfiguration(server);
  let log = new Log(config.app_name, server, 'do_action');
  const client = getElasticsearchClient({server, config});

  /* Email Settings */
  let email;
  try {
    email = new EmailClient(config.settings.email);
  } catch (err) {
    log.error('email client: ' + err.toString());
    logHistory({
      server,
      watcherTitle: task._source.title,
      message: 'email client: ' + err.toString(),
      level: 'high',
      isError: true,
    });
  }

  /* Slack Settings */
  let slack;
  try {
    if (config.settings.slack.active) {
      slack = new WebClient(config.settings.slack.token);
    }
  } catch (err) {
    log.error('slack client: ' + err.toString());
    logHistory({
      server,
      watcherTitle: task._source.title,
      message: 'slack client: ' + err.toString(),
      level: 'high',
      isError: true,
    });
  }

  /* Debounce Function, returns true if throttled */
  var getDuration = require('sum-time');
  var debounce = function (id, period) {
    var duration = getDuration(period);

    var isInThrottlePeriod = function (id, now, duration) {
      return (now - server.sentinlStore.actions[id]) < duration;
    };

    if (duration) {
      var justNow = new Date().getTime();

      if (!_.has(server.sentinlStore, 'actions')) {
        server.sentinlStore.actions = {};
      }

      if (!_.has(server.sentinlStore.actions, id) || !isInThrottlePeriod(id, justNow, duration)) {
        server.sentinlStore.actions[id] = justNow;
        return false;
      }

      // throttle action
      return true;
    } else {
      return false;
    }
  };

  if (task._source.dashboard_link) {
    task._source.dashboard_link = _updateDashboardLinkTimeRange(task._source.dashboard_link, moment().subtract(1, 'hour'), moment());
  }

  /* Loop Actions */
  _.forEach(actions, function (action, actionId) {
    log = new Log(config.app_name, server, task._id);
    log.debug('processing action');

    const actionName = action.name || actionId; // advanced watcher can be without name property

    /* ***************************************************************************** */
    /*
    *   "console" : {
    *      "priority" : "low",
    *      "message" : "Average {{payload.aggregations.avg.value}}",
    *    }
    */

    var priority;
    if (action.console) {
      (async () => {
        try {
          priority = action.console.priority || 'medium';
          let formatterConsole = action.console.message ? action.console.message : '{{ payload }}';
          let message = mustache.render(formatterConsole, {payload: payload,});
          log.debug('console payload', payload);

          await logHistory({
            server,
            watcherTitle: task._source.title,
            actionName,
            message: toString(message),
            level: priority,
            payload: !task._source.save_payload ? {} : payload,
          });
        } catch (err) {
          log.error('console action: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'console action: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    /* Throttle Action based on 'throttle_period' optional parameter
    /* "throttle_period": "5m"
    /*
    /* ***************************************************************************** */
    if (action.throttle_period) {
      (async () => {
        try {
          const id = `${task._id}_${actionId}`;
          if (debounce(id, action.throttle_period)) {
            log.info(`action throttled, watcher id: ${task._id}, action name: ${actionId}`);
            await logHistory({
              server,
              watcherTitle: task._source.title,
              actionName,
              message: `Action Throttled for ${action.throttle_period}`,
              level: priority,
              payload: {}
            });
            return;
          }
        } catch (err) {
          log.error('throttle: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'throttle: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    *   "email" : {
    *      "to" : "<EMAIL>@localhost",
    *      "from" : "<EMAIL>",
    *      "subject" : "Alarm Title",
    *      "priority" : "high",
    *      "body" : "Series Alarm {{ payload._id}}: {{payload.hits.total}}",
    *      "stateless" : false,
    *     }
    */

    var formatterSubject;
    var formatterBody;
    var subject;
    var text;
    if (action.email) {
      (async () => {
        try {
          formatterSubject = action.email.subject || ('SENTINL: ' + actionId);

          formatterBody = action.email.body;
          if (!formatterBody) {
            if (payload.docs) {
              formatterBody = 'Number of documents: {{payload.docs.length}}';
            } else if (payload.sheet) {
              formatterBody = 'Number of 0 sheet data: {{payload.sheet[0].list[0].data.length}}';
            } else { // hits
              formatterBody = 'Series Alarm {{payload._id}}: {{payload.hits.total}}';
            }
          }

          subject = mustache.render(formatterSubject, {payload: payload, watcher: task._source});
          text = mustache.render(formatterBody, {payload: payload, watcher: task._source});
          priority = action.email.priority || 'medium';


          if (!action.email.stateless) {
            // Log Event
            await logHistory({
              server,
              watcherTitle: task._source.title,
              actionName,
              message: toString(text),
              level: priority,
              payload: !task._source.save_payload ? {} : payload,
            });
          }

          log.info('sending email');

          if (!config.settings.email.active) {
            throw new Error('email delivery disabled');
          } else {
            await email.send({
              text,
              from: action.email.from,
              to: action.email.to,
              subject
            });
          }
        } catch (err) {
          log.error('email action: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'email action: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }


    /* ***************************************************************************** */
    /*
    *   "email_html" : {
    *      "to" : "<EMAIL>",
    *      "from" : "<EMAIL>",
    *      "subject" : "Alarm Title",
    *      "priority" : "high",
    *      "body" : "Series Alarm {{ payload._id}}: {{payload.hits.total}}",
    *      "html" : "<p>Series Alarm {{ payload._id}}: {{payload.hits.total}}</p>",
    *      "stateless" : false,
    *     }
    */
    var html;
    if (action.email_html) {
      (async () => {
        try {
          formatterSubject = action.email_html.subject ? action.email_html.subject : 'SENTINL: ' + actionId;

          formatterBody = action.email_html.body;
          if (!formatterBody) {
            if (payload.docs) {
              formatterBody = 'Number of documents: {{payload.docs.length}}';
            } else if (payload.sheet) {
              formatterBody = 'Number of 0 sheet data: {{payload.sheet[0].list[0].data.length}}';
            } else { // hits
              formatterBody = 'Series Alarm {{payload._id}}: {{payload.hits.total}}';
            }
          }

          let formatterConsole = action.email_html.html || '<p>Series Alarm {{ payload._id}}: {{payload.hits.total}}</p>';
          subject = mustache.render(formatterSubject, {payload: payload, watcher: task._source});
          text = mustache.render(formatterBody, {payload: payload, watcher: task._source});
          html = mustache.render(formatterConsole, {payload: payload, watcher: task._source});
          priority = action.email_html.priority || 'medium';

          if (!action.email_html.stateless) {
            // Log Event
            await logHistory({
              server,
              watcherTitle: task._source.title,
              actionName,
              message: toString(text),
              level: priority,
              payload: !task._source.save_payload ? {} : payload,
            });
          }

          log.info('processing html email');

          if (!config.settings.email.active) {
            throw new Error('email delivery disabled');
          } else {
            await email.send({
              text,
              from: action.email_html.from,
              to: action.email_html.to,
              subject,
              attachment: [
                {
                  data: html,
                  alternative: true
                }
              ]
            });
          }
        } catch (err) {
          log.error('html email action: ' + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'html email action: ' + err.toString(),
            level: 'high',
            isError: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    *   "report" : {
    *      "to" : "root@localhost",
    *      "from" : "sentinl@localhost",
    *      "subject" : "Report Title",
    *      "priority" : "high",
    *      "body" : "Series Report {{ payload._id}}: {{payload.hits.total}}",
    *      "snapshot" : {
    *         "res" : "1280,900",
    *         "url" : "http://127.0.0.1/app/kibana#/dashboard/Alerts",
    *         "path" : "/tmp/",
    *         "params" : {
    *             "username" : "username",
    *             "password" : "password",
    *             "delay" : 15,
    *             "crop" : false
    *         }
    *       },
    *      "stateless" : false
    *    }
    */
    if (action.report) {
      (async () => {
        try {
          if (!config.settings.report.active) {
            throw new Error('Reports disabled');
          }

          await reportAction({
            server,
            action,
            watcherTitle: task._source.title,
            esPayload: payload,
            actionName,
            emailClient: email,
          });
        } catch (err) {
          log.error(`${task._source.title}, report action: ` + err.toString());
          logHistory({
            server,
            watcherTitle: task._source.title,
            message: 'report action: ' + err.toString(),
            level: 'high',
            isError: true,
            isReport: true,
            actionName,
          });
        }
      })();
    }

    /* ***************************************************************************** */
    /*
    *   "slack" : {
    *      "channel": '#<channel>',
    *      "message" : "Series Alarm {{ payload._id}}: {{payload.hits.total}}",
    *      "stateless" : false,
    *    }
    */

    if (action.slack) {
      (async () => {
        try {
          let formatter = action.slack.message ? action.slack.message : 'Series Alarm {{ payload._id}}: {{payload.hits.total}}';
          let message = mustache.render(formatter, {payload: payload, watcher: task._source});
          priority = action.slack.priority || 'medium';
{'fraction_non_alphanumeric': 0.17421705369300408, 'fraction_numerical': 0.0025760865565082986, 'mean_word_length': 1.9119159879982854, 'pattern_counts': {'":': 4, '<': 31, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7535854', 'n_tokens_mistral': 7595, 'n_tokens_neox': 7405, 'n_words': 1716}