merge-nodes.js

/**
 * @module merge_nodes
 * @description The script was used to merge nodes with keys.
 * The script will based on `change-rules.js`.
 * @param {string[]} [keys = []] - The proxy name include keys will be merged.
 */

const { readFileSync, existsSync } = require('fs');
const { resolve } = require('path');
var modules_path = resolve(__dirname, '../node_modules');
const variable_path = resolve(__dirname, './variables.yml');
let debug = false;

let merge_nodes = async (raw, { yaml, console, notify }, { url, name }) => {
  try {
    // check nodes_modules
    if (existsSync(modules_path)) {
      var _notify = require('../lib/notify');
      notify = _notify.notify;
    } else console.log('[warning]: no found node_modules');

    // check yaml
    try {
      var rawObj = yaml.parse(raw);
    } catch (e) {
      if (
        e.message === 'Implicit map keys need to be on a single line' &&
        !new RegExp('^((?!www.example.com).)*$').test(url)
      ) {
        console.log('[warning]: raw is not yaml');
        rawObj = { proxies: [], 'proxy-groups': [], rules: [] };
      } else {
        console.log('[error]: check yaml fail');
        console.log(e);
        throw e;
      }
    }

    //check variables.yml
    if (!existsSync(variable_path)) {
      console.log('[warning]: no found ./scripts/variables.yml');
      return yaml.stringify(rawObj);
    }
    var _variables = yaml.parse(readFileSync(variable_path, 'utf-8'));
    if (!_variables['merge_nodes']) {
      console.log('[warning]: no found merge_nodes variables');
      return yaml.stringify(rawObj);
    } else var variables = _variables['merge_nodes'];
    raw = yaml.stringify(rawObj);

    console.log(`[info]: start merge nodes in ${name} at ${new Date()}`);
    if (variables.length != 0) {
      console.log('[info]: merge_nodes variables:');
      console.log(yaml.stringify(variables, null, 2));
      let _proxy = ['DIRECT', '♻️ AUTO'];
      let _auto = [];
      let _other = [];
      for (let i = 0; i < variables.length; i++) {
        _other.push({
          name: variables[i]['name'],
          type: 'url-test',
          url: 'http://www.gstatic.com/generate_204',
          interval: 300,
          proxies: []
        });
      }
      _other.push({
        name: '🐟 OTHER',
        type: 'url-test',
        url: 'http://www.gstatic.com/generate_204',
        interval: 300,
        proxies: []
      });
      if (debug) {
        console.log(`[debug]: ${yaml.stringify(_other)}`);
        console.log(`[debug]: ${yaml.stringify(rawObj['proxies'])}`);
      }
      if (rawObj['proxies']) {
        rawObj['proxies'].forEach(proxy => {
          if (debug) console.log(`[debug]: proxy['name']: ${proxy['name']}`);
          let check = false;
          for (let i = 0; i < variables.length; i++) {
            if (debug) console.log(`[debug]: variables[${i}]: ${JSON.stringify(variables[i])}`);
            variables[i]['keys'].forEach(key => {
              if (proxy['name'].search(key) != -1) {
                if (debug) console.log(`[debug]: add ${proxy['name']} into ${_other[i]['name']}`);
                _other[i]['proxies'].push(proxy['name']);
                check = true;
              }
            });
            if (i === variables.length - 1 && check === false) {
              if (debug) console.log(`[debug]: add ${proxy['name']} into ${_other[i + 1]['name']}`);
              _other[i + 1]['proxies'].push(proxy['name']);
            }
          }
        });
      } else {
        return yaml.stringify(rawObj);
      }
      if (debug) {
        console.log(`[debug]: _other[${_other.length}]:`);
        console.log(yaml.stringify(_other));
      }
      let _other_filter = _other.filter(item => item['proxies'].length != 0);
      if (debug) {
        console.log(`[debug]: _other_filter[${_other_filter.length}]:`);
        console.log(yaml.stringify(_other_filter));
      }
      rawObj['proxy-groups'][0]['proxies'] = yaml.parse(
        yaml.stringify(_proxy.concat(_other_filter.map(item => item['name'])))
      );
      rawObj['proxy-groups'][1]['proxies'] = yaml.parse(
        yaml.stringify(_auto.concat(_other_filter.map(item => item['name'])))
      );
      if (rawObj['proxy-groups'][1]['proxies'].indexOf('🐟 OTHER') != -1) {
        let other_index = rawObj['proxy-groups'][1]['proxies'].indexOf('🐟 OTHER');
        rawObj['proxy-groups'][1]['proxies'].splice(other_index, 1);
        console.log(`[info]: delete 🐟 OTHER in ♻️ AUTO`);
      }
      if (rawObj['proxy-groups'][1]['proxies'].indexOf('🎶 MUSIC') != -1) {
        let other_index = rawObj['proxy-groups'][1]['proxies'].indexOf('🎶 MUSIC');
        rawObj['proxy-groups'][1]['proxies'].splice(other_index, 1);
        console.log(`[info]: delete 🎶 MUSIC in ♻️ AUTO`);
      }
      rawObj['proxy-groups'] = yaml.parse(
        yaml.stringify(rawObj['proxy-groups'].concat(_other_filter))
      );
    } else {
      console.log('[warning]: keys need to set.');
      console.log(`[info]: ${name} merge nodes completely`);
    }
    return yaml.stringify(rawObj);
  } catch (e) {
    console.log(`[error]: ${e}`);
    notify(`merge-nodes failed`, e.message);
    throw e;
  }
};

module.exports.parse = merge_nodes;