Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
EdwonLim committed Oct 8, 2018
0 parents commit 1a78f37
Show file tree
Hide file tree
Showing 11 changed files with 338 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
2 changes: 2 additions & 0 deletions bin/qscan
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('../src/cli');
20 changes: 20 additions & 0 deletions example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const QScan = require('../src/index');
const scan = new QScan({
modelOpts: {
'wx-default': {
udid: 'xxx',
port: '4723',
opts: {
user: 'xxx',
pass: 'xxx'
}
}
}
});

scan.run({
modelName: 'wx-default',
type: 'ide-login-scan'
}, (err) => {

});
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "qscan",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"bin": {
"qscan": "./bin/qscan"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "Apache 2.0",
"dependencies": {
"async": "^2.6.1",
"commander": "^2.18.0",
"queue": "^4.5.0",
"wd": "^1.10.3"
}
}
26 changes: 26 additions & 0 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const fs = require('fs');
const path = require('path');
const prog = require('commander');

const pkgJSON = require('../package.json');

prog.version(pkgJSON.version, '-v, --version')
.usage('<command> [options]');

fs.readdirSync(path.join(__dirname, './commands')).forEach(file => {
if (/\.js$/.test(file)) {
const cmd = require(`./commands/${file}`);
const name = path.basename(file, '.js');
const program = prog.command(name)
.usage(cmd.usage)
.description(cmd.description)
.action(cmd.action);

}
});

prog.parse(process.argv);

if (!program.args.length) {
program.help();
}
10 changes: 10 additions & 0 deletions src/commands/doctor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
usage: '[options]',
description: '检查运行环境',
options: {
'-l, --login': '对登录逻辑进行检查'
},
action: (rc, options) => {

}
};
14 changes: 14 additions & 0 deletions src/commands/scan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
usage: '[options]',
description: '扫描二维码',
options: {
'-u, --uri': '二维码地址',
'-f, --file': '二维码本地图片文件地址',
'-m, --model': '使用的扫码模式',
'-t, --type': '使用的扫码类型',
'-d, --device': '指定设备的 UDID'
},
action: (rc, options) => {

}
};
10 changes: 10 additions & 0 deletions src/commands/serve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
usage: '[options]',
description: '启动自助二维码扫描服务',
options: {
'-p, --port': '服务端口'
},
action: (rc, options) => {

}
};
176 changes: 176 additions & 0 deletions src/core/qcan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
const fs = require('fs');
const path = require('path');
const wd = require('wd');
const async = require('async');

const Queue = require('queue');
const EventEmitter = require('events').EventEmitter;

// 本地 Host
const LOCAL_HOST = '127.0.0.1';
// 默认的等待时间
const WAIT_TIMEOUT = 3000;
// 队列配置
const QUEUE_OPTS = {
concurrency: 1, // 并发任务数1,等于串行
timeout: 10 * 60 * 1000, // 单个任务超时,10分钟
autostart: true // 有任务自动执行
};
// 默认的 Model
const DEFAULT_MODEL_PATH = path.join(__dirname, '../models');
// 默认的配置文件路径
const DEFAULT_MODEL_OPTS_PATH = path.join(process.env['HOME'], '.qscanrc');

class QScan extends EventEmitter {
constructor({customModel, modelOpts}) {
// Models
this.models = {};
// 队列
this.queues = {};
// 读取配置
if (modelOpts) {
if (typeof modelOpts === 'string' && fs.existsSync(modelOpts)) {
modelOpts = JSON.parse(fs.readFileSync(modelOpts, 'UTF-8'));
}
} else if (fs.existsSync(DEFAULT_MODEL_OPTS_PATH)) {
modelOpts = JSON.parse(fs.readFileSync(DEFAULT_MODEL_OPTS_PATH, 'UTF-8'));
} else {
modelOpts = {};
}
// 读取默认的 Model
fs.readdirSync(DEFAULT_MODEL_PATH, (file) => this.loadModelFile({
modelFilePath: path.join(DEFAULT_MODEL_PATH, file),
modelOpts
}));
// 读取自定义 model
if (customModel) {
if (typeof customModel === 'string' && fs.existsSync(customModel)) {
fs.readdirSync(customModel, (file) => this.__loadModelFile({
modelFilePath: path.join(customModel, file),
modelOpts
}));
} else {
Object.keys(customModel).forEach((key) => {
const model = customModel[key];
const opts = modelOpts[model.name || 'default'] || {};
this.loadModel({
model,
...opts
});
});
}
}
}
doctor(modelName, cb) {
const tasks = [];
tasks.push((cb) => {
// TODO Check Appium
});
if (modelName && this.models[modelName]) {
const model = this.models[modelName];
if (model.udid) {
tasks.push((cb) => {
// TODO Check Devices
// model.udid
});
}
if (model.port) {
tasks.push((cb) => {
// TODO Check Appium Process
});
}
if (model.checkApp) {
tasks.push((cb) => model.checkApp(cb));
}
}
async.series(tasks, cb);
}
loadModel({model, udid, port, opts}) {
if (udid) {
model.udid = model.connectOpt.udid = udid;
}
if (port) {
model.port = port;
}
if (opts) {
model.opts = opts;
}
this.models[model.name] = model;
}
run({modelName, type}, cb) {
if (!this.queues[modelName]) {
this.queues[modelName] = new Queue(QUEUE_OPTS);
}
this.queues[modelName].push((callback) => {
__handleDevice({modelName, type, reset: false}, (err) => {
cb(err);
callback();
});
});
}
__loadModelFile({modelFilePath, modelOpts}) {
const model = require(modelFilePath);
const opts = modelOpts[model.name || 'default'] || {};
this.loadModel({
model,
...opts
});
}
__connectAppium(model, cb) {
//TODO Connect for Start Appium
setTimeout(() => {
cb(null);
}, 100);
}
__initConnect(model, reset) {
return wd.promiseChainRemote({
host: LOCAL_HOST,
port: model.port,
}).init(Object.assign({}, model.connectOpt, {
noReset: !reset
})).setImplicitWaitTimeout(model.waitTimeout || WAIT_TIMEOUT);
}
__initModel(model, reset) {
return model.init(this.__initConnect(model, reset));
}
__checkStatus(model, cb) {
model.checkStatus(this.__initConnect(model), model.opts, cb);
}
__handleDevice({modelName, type, reset}, cb) {
const model = this.models[modelName];
if (model) {
if (model.types && model.types[type]) {
this.__connectAppium(model, (err) => {
if (err) {
cb(err);
} else if (reset) {
model.types[type](this.__initModel(model, true), model.opts).catch((e) => {
cb(e);
}).finnally(() => {
cb(null);
});
} else {
this.__checkStatus(model, (err, flag) => {
if (!err && flag) {
model.types[type](this.__initModel(model, false), model.opts).catch((e) => {
cb(e);
}).finnally(() => {
cb(null);
});
} else {
this.__handleDevice({modelName, type, reset: true}, cb);
}
});
}
});
} else {
cb(new Error(`Not Found This Type <${type}>.`));
}
} else {
cb(new Error(`Not Found This Model <${modelName}>.`));
}
}
}

module.exports = QScan;

16 changes: 16 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const QScan = require('./core/qcan');

// 中间件
QScan.middleWare = ({customModel, modelOpts}) => {
const scan = new QScan({customModel, modelOpts});
return (req, res) => {
// TODO
};
};

// 服务
QScan.serve = ({customModel, modelOpts, port, forever}) => {
// TODO
};

module.exports = QScan;
43 changes: 43 additions & 0 deletions src/models/wx-default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module.exports = {
// Model Name
name: 'wx-default',
// 参数,包括用户名,密码等
opts: {},
// Appium 连接参数
connectOpt: {
platformName: 'Android',
deviceName: 'Android',
appPackage: 'com.tencent.mm',
},
// 等待超时时间
waitTimeout: 3000,
// 检查 App 是否是相应的版本等
checkApp: (cb) => {
const version = '6.7.0';
setTimeout(() => {
cb(null, true);
}, 100);
},
// 初始化 App,从打开、登录到主界面
init: (app) => {
return app;
},
// 检查状态是否正确,包括登录的用户是指定用户等
checkStatus: (app, opts, cb) => {
setTimeout(() => {
cb(null, true);
}, 100);
},
// 不同类型的扫码
types: {
// 开发者工具登录
"ide-login-scan": (app, opts) => {
return app;
},
// 微信后台登录
"backstage-login-scan": (app, opts) => {
return app;
}
}
};

0 comments on commit 1a78f37

Please sign in to comment.