/** * 中国蚁剑::核心模块::基础函数库 * 开写:2016/04/12 * 更新:- * 作者:蚁逅 */ 'use strict'; const iconv = require('iconv-lite'); class Base { /** * 初始化 * @param {Object} opts 配置对象 * @return {Object} this */ constructor(opts) { // 默认配置 opts['encode'] = opts['encode'] || 'utf8'; opts['encoder'] = opts['encoder'] || 'default'; this.__opts__ = opts; this['__encoder__'] = { /** * 默认编码器 * @param {String} pwd 连接密码 * @param {Object} data 请求数据 * @return {Object} 生成数据 */ default(pwd, data) { data[pwd] = data['_']; delete data['_']; return data; }, /** * 随机编码器 * @param {String} pwd 连接密码 * @param {Object} data 请求数据 * @return {Object} 生成数据 */ random(pwd, data) { let _encoders = []; for (let _ in this) { if (_ === 'random') { continue } _encoders.push(_); } let _index = parseInt(Math.random() * _encoders.length); return this[ _encoders[_index] ](pwd, data); } } // 解析自定义编码器 this.user_encoders.map((_)=>{ this.parseEncoder(`${_}`); }); } /** * 返回参数列表 * @return {array} [arg1, arg2, arg3..] */ argv() { // 生成一个随机的变量名 let random = () => `0x${(Math.random() + Math.random()).toString(16).substr(2)}`; // 返回六个随机变量名数组 return [ random(), random(), random(), random(), random(), random() ]; } /** * 字符串格式化处理 * 使用方法:const format = new format() * @param {String} encode [字符串编码,默认utf8] * @return {Object} [返回字符串处理函数对象] */ format(encode) { return { /** * base64编码 * @param {String} str 字符串 * @return {String} 编码后的字符串 */ base64(str) { return new Buffer( iconv.encode(new Buffer(str), encode) ).toString('base64'); }, /** * 字符串转16进制(不进行编码转换 * @param {String} str 转换的字符串 * @return {Buffer} 转换完成的buffer */ buffer(str) { return new Buffer(str).toString('hex').toUpperCase(); }, /** * 字符串转16进制(进行编码转换 * @param {String} str 转换的字符串 * @return {Buffer} 转换完成的buffer */ hex(str) { return new Buffer( iconv.encode(new Buffer(str), encode) ).toString('hex').toUpperCase(); } } } /** * 解析脚本模板 * @param {String} tpl 模板文件 * @return {Object} 解析完毕的模板对象(参数`_`为主代码入口 */ parseTemplate(tpl) { // 把模板路径的`/`转换为`_` let templateName = (tpl.split('template/')[1]).replace(/\//g, '_'); this[templateName] = {}; // 加载模板 let _argv = this.argv(); let templateObj = require(`${tpl}`)( _argv[0], _argv[1], _argv[2], _argv[3], _argv[4], _argv[5] ); // let formatter = new this.format(this.__opts__['encode']); let formatter = Base.prototype.format(this.__opts__['encode']); // 解析模板 for (let funcName in templateObj) { this[templateName][funcName] = ( (args) => { if (typeof(args) === 'object') { // 如果脚本函数需要参数,则进行解析 return (argv) => { let data = {}; // 克隆源数据到返回数据中 for (let _ in args) { data[_] = args[_]; } // 循环替换脚本中的标签 for (let arg in args) { (args[arg].match(/#{([\w\:]+)}/g) || []).map( // example: #{hex::str} = hex(str), #{arg1} = arg1 (tag) => { let tagStr = tag.substr(2, tag.length - 3); let tagArr = tagStr.split('::'); let func, retStr; if ( (tagArr.length > 0) && (func = formatter[tagArr[0]]) ) { // 如果包含有分割标签且该格式化函数存在,则调用该函数进行处理 retStr = func( argv[tagArr[1] || ''] ); } else { // 否则替换直接返回字符串 retStr = argv[tagStr] || ''; } // 组合最终生成模板代码 data[arg] = args[arg].replace(tag, retStr); } ) } // 发送HTTP请求 return data; } } else { // 否则直接返回 return () => ({ _: args }); } } )(templateObj[funcName]); } } /** * 解析编码器 * ? 编码器其实模板返回的脚本对象进行编码处理,主要就是把脚本模板中的`_`主变量改为用户传递的密码 * @param {String} enc 编码器文件 * @return {Object} 编码器处理函数对象 */ parseEncoder(enc) { // 加载编码器 // QAQ!我也不知道为什么,如果直接require变量名,babel编译就会warning,so我只好加个`咯~ this['__encoder__'][enc.split('encoder/')[1]] = require(`${enc}`); } /** * 编码处理并返回操作 * @param {String} tag_s 前截断符 * @param {String} tag_e 后截断符 * @param {Object} data 源POST数据 * @return {Object} 最终生成数据// 将返回三个参数对象:tag_s,tag_e,data */ encodeComplete(tag_s, tag_e, data) { // 编码器处理 let finalData = this.__encoder__[this.__opts__['encoder']]( this.__opts__['pwd'], data ); return { 'tag_s': tag_s, 'tag_e': tag_e, 'data': finalData }; } /** * HTTP请求函数 * ? 用法:core.request(core.base.info()).then((ret) => {}).catch((e) => {}).. * @param {Object} code 请求源数据 * @param {Function} chunkCallBack 二进制流回调函数,可选 * @return {Promise} Promise操作对象 */ request(code, chunkCallBack) { const opt = this.complete(code); return new Promise((res, rej) => { // 随机ID(用于监听数据来源) const hash = (String(+new Date) + String(Math.random())).substr(10, 10).replace('.', '_'); // 监听数据返回 antSword['ipcRenderer'] // 请求完毕返回数据{text,buff} .once(`request-${hash}`, (event, ret) => { return res({ 'text': ret['text'], 'buff': ret['buff'] }); }) // HTTP请求返回字节流 .on(`request-chunk-${hash}`, (event, ret) => { return chunkCallBack ? chunkCallBack(ret) : null; }) // 数据请求错误 .once(`request-error-${hash}`, (event, ret) => { return rej(ret); }) // 发送请求数据 .send('request', { url: this.__opts__['url'], hash: hash, data: opt['data'], tag_s: opt['tag_s'], tag_e: opt['tag_e'], encode: this.__opts__['encode'], ignoreHTTPS: (this.__opts__['otherConf'] || {})['ignore-https'] === 1, timeout: parseInt((this.__opts__['otherConf'] || {})['request-timeout']), headers: (this.__opts__['httpConf'] || {})['headers'] || {}, body: (this.__opts__['httpConf'] || {})['body'] || {} }); }) } /** * 文件下载专用函数(因有些文件过大,导致Electron出错 * @param {String} savePath 保存文件路径 * @param {Object} postCode 提交数据 * @param {Function} progressCallback 进度回调 * @return {Promise} Promise操作对象 */ download(savePath, postCode, progressCallback) { const opt = this.complete(postCode); return new Promise((ret, rej) => { // 随机ID(用于监听数据来源) const hash = (String(+new Date) + String(Math.random())).substr(10, 10).replace('.', '_'); // 监听数据返回 antSword['ipcRenderer'] // 请求完毕返回数据(size) .once(`download-${hash}`, (event, size) => { return ret(size); }) // HTTP请求返回字节流大小 .on(`download-progress-${hash}`, (event, size) => { return progressCallback ? progressCallback(size) : null; }) // 数据请求错误 .once(`download-error-${hash}`, (event, ret) => { throw new Error(ret); }) // 发送请求数据 .send('download', { url: this.__opts__['url'], hash: hash, path: savePath, data: opt['data'], tag_s: opt['tag_s'], tag_e: opt['tag_e'], encode: this.__opts__['encode'] }); }) } } // export default Base; module.exports = Base;