Skip to content

Commit

Permalink
feat(all): 脚本2.0与软件4.0更新完毕
Browse files Browse the repository at this point in the history
  • Loading branch information
enncy committed Mar 1, 2023
1 parent 961235f commit 6e04369
Show file tree
Hide file tree
Showing 105 changed files with 4,993 additions and 1,581 deletions.
1 change: 1 addition & 0 deletions packages/app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pnpm-lock.yaml
package-lock.json
public/
bin/

# rollup-plugin-visualizer
stats.html
Expand Down
14 changes: 10 additions & 4 deletions packages/app/electron.builder.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
{
"$schema": "https://json.schemastore.org/electron-builder.json",
"appId": "ocs.enncy.cn",
"productName": "ocs",
"extraMetadata": {
"name": "OCS Desktop",
"description": "OCS 浏览器自动化神器,一键浏览器多开,用户脚本环境一键配置。"
},
"productName": "OCS Desktop",
"asar": false,
"copyright": "Copyright © 2021 ${author}",
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},

"win": {
"icon": "public/favicon.png",
"artifactName": "${productName}-${version}-setup-${os}-${arch}.${ext}",
"artifactName": "ocs-${version}-setup-${os}-${arch}.${ext}",
"target": [
{
"target": "nsis",
"arch": ["x64"]
},
{
"target": "zip"
"target": "nsis",
"arch": ["ia32"]
}
]
},
"mac": {
"icon": "public/favicon.icns",
"artifactName": "${productName}-${version}-setup-${os}-${arch}.${ext}",
"artifactName": "ocs-${version}-setup-${os}-${arch}.${ext}",
"target": "default"
}
}
12 changes: 5 additions & 7 deletions packages/app/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @ts-check

import { app } from 'electron';
import { handleOpenFile } from './src/tasks/handle.open';
import { remoteRegister } from './src/tasks/remote.register';
import { initStore } from './src/tasks/init.store';
import { autoLaunch } from './src/tasks/auto.launch';
Expand All @@ -12,9 +11,12 @@ import { handleError } from './src/tasks/error.handler';
import { updater } from './src/tasks/updater';
import { startupServer } from './src/tasks/startup.server';

app.setName('ocs');

// 设置 webrtc 的影像帧率比例,最高100,太高会造成卡顿,默认50
app.commandLine.appendSwitch('webrtc-max-cpu-consumption-percentage', '1');
app.disableHardwareAcceleration();
// 看网上争议比较多这里开启硬件加速试试
// app.disableHardwareAcceleration();

/** 获取单进程锁 */
const gotTheLock = app.requestSingleInstanceLock();
Expand All @@ -34,8 +36,6 @@ function bootstrap() {
task('启动接口服务', () => startupServer());
}),
task('初始化自动启动', () => autoLaunch()),
task('处理打开文件', () => handleOpenFile(process.argv)),

task('启动渲染进程', async () => {
await app.whenReady();
const window = createWindow();
Expand All @@ -50,6 +50,7 @@ function bootstrap() {
e.preventDefault();
window.webContents.send('close');
});

task('初始化远程通信模块', () => remoteRegister(window));
task('注册app事件监听器', () => globalListenerRegister(window));

Expand All @@ -60,9 +61,6 @@ function bootstrap() {
window.webContents.openDevTools();
}

window.webContents.setZoomFactor(1);
window.show();

if (app.isPackaged) {
task('软件更新', () => updater(window));
}
Expand Down
4 changes: 3 additions & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"node-forge": "1.0.0",
"playwright-core": "^1.28.0",
"semver": "^7.3.5",
"tesseract.js": "^2.1.5"
"systeminformation": "^5.17.10",
"tesseract.js": "^2.1.5",
"xlsx": "^0.17.5"
},
"repository": {
"type": "git",
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/scripts/automation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './wk/index';
42 changes: 42 additions & 0 deletions packages/app/src/scripts/automation/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Page } from 'playwright-core';
import { EventsRecord } from '../interface';
import { AutomationScript, RunFunction, ScriptOptions } from '../script';

export type BaseAutomationEvents = {
'script-error': (...msg: string[]) => void;
'script-data': (...msg: string[]) => void;
};

export interface Config {
label: string;
value: any;
hide?: boolean;
}

export type ConfigValueRecord<C extends Record<string, Config>> = {
[K in keyof C]: C[K]['value'];
};

export class ConfigsRequiredAutomationScript<
C extends Record<string, Config> = Record<string, Config>,
RF extends RunFunction = RunFunction,
E extends EventsRecord = EventsRecord
> extends AutomationScript<RF, E> {
name: string;
configs: C;

constructor(configs: C, options: ScriptOptions<RF>) {
super(options);
this.name = options.name;
this.run = options.run;
this.configs = configs;
}
}

export class PlaywrightScript<
C extends Record<string, Config> = Record<string, Config>
> extends ConfigsRequiredAutomationScript<
C,
(page: Page, configs: ConfigValueRecord<C>) => any,
BaseAutomationEvents
> {}
176 changes: 176 additions & 0 deletions packages/app/src/scripts/automation/wk/cx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import type { Page } from 'playwright-core';
import { BaseAutomationEvents, PlaywrightScript } from '../interface';
import { slowType } from '../../utils';
import axios from 'axios';
import { TypedEventEmitter } from '../../interface';

export const CXUnitLoginScript = new PlaywrightScript(
{
unit: {
label: '学校/单位',
value: ''
},
id: {
label: '学号/工号',
value: ''
},
password: {
label: '密码',
value: ''
}
},
{
name: '超星-学校机构登录',
async run(page, configs) {
try {
if (await isNotLogin(page)) {
/** 其他登录 */
await Promise.all([page.waitForLoadState('networkidle'), page.click('#otherlogin')]);
await page.waitForTimeout(3000);
/** 输入机构名, 并等待搜索结果 */
await slowType(page, '#inputunitname', configs.unit);
await page.waitForTimeout(2000);
/** 点击第一个结果 */
await page.click('.filter-list > ul > li');
await page.type('#uname', configs.id);
await page.type('#password', configs.password);

await login(page, CXUnitLoginScript, {
ocrApiUrl: 'http://localhost:15319/ocr',
ocrApiImageKey: 'ocr'
});
}
} catch (err) {
CXUnitLoginScript.emit('script-error', String(err));
}
}
}
);

export const CXPhoneLoginScript = new PlaywrightScript(
{
phone: {
label: '手机号',
value: ''
},
password: {
label: '密码',
value: ''
}
},
{
name: '超星-手机密码登录',
async run(page, configs) {
try {
if (await isNotLogin(page)) {
await page.type('#phone', configs.phone);
await page.type('#pwd', configs.password);

await login(page, CXPhoneLoginScript);
}
} catch (err) {
CXPhoneLoginScript.emit('script-error', String(err));
}
}
}
);

function login(
page: Page,
events: TypedEventEmitter<BaseAutomationEvents>,
opts?: {
ocrApiUrl?: string;
ocrApiImageKey?: string;
}
) {
return new Promise<void>((resolve, reject) => {
// 监听登录状态
const listenerLogin = (page: Page) => {
// 登录成功
if (page.url().includes('/space/index') || page.url().includes('/base')) {
page.off('load', listenerLogin);
resolve();
}
};

page.on('load', listenerLogin);
// 1分钟登录超时
const timeout = setTimeout(() => {
events.emit('script-error', '登录超时,请重试。');
resolve();
}, 60 * 1000);

// 重试5次
let tryCount = 5;
// 尝试登录
const tryLogin = async () => {
tryCount--;
const area = await page.$('#numVerCode');
if (area !== null) {
/** 破解验证码 */
if (opts?.ocrApiUrl && opts?.ocrApiImageKey && area) {
/** 每次都点击保证是最新图片 */
await Promise.all([page.waitForLoadState('networkidle'), await area.click()]);
const box = await area.boundingBox();
if (box) {
/** 请求验证码破解接口 */
const body = Object.create([]);
const buffer = await page.screenshot({ clip: box });
Reflect.set(body, opts.ocrApiImageKey, buffer.toString('base64'));
const {
data: { ocr: code, canOCR }
} = await axios.post(opts.ocrApiUrl, body);
if (canOCR) {
/** 破解验证码 */
if (code) {
await page.fill('#vercode', code);
}
} else {
throw new Error('未检测到图片验证码识别模块, 请手动输入验证码。');
}
}
}
}

// 点击登录
await page.click('#loginBtn');
await page.waitForTimeout(3000);

// 检测错误
const vercodeMsg = await page.evaluate(() =>
Array.from(document.querySelectorAll('#vercodeMsg.err-txt'))
.map((e) => e.textContent?.trim() || '')
.filter(Boolean)
);

const errors = await page.evaluate(() =>
Array.from(document.querySelectorAll('.err-txt,.err-tip'))
.map((e) => e.textContent?.trim() || '')
.filter(Boolean)
);

if (vercodeMsg.concat(errors).some((str) => str.includes('验证码'))) {
if (tryCount === 0) {
clearTimeout(timeout);
events.emit('script-error', vercodeMsg.join('\n').trim());
} else {
await tryLogin();
}
} else if (errors.length) {
clearTimeout(timeout);
events.emit('script-error', errors.join('\n').trim());
} else {
clearTimeout(timeout);
}
};

tryLogin();
});
}

async function isNotLogin(page: Page) {
await page.goto('https://i.chaoxing.com');
await page.waitForTimeout(2000);

return page.url().includes('passport2.chaoxing.com');
}
2 changes: 2 additions & 0 deletions packages/app/src/scripts/automation/wk/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './cx';
export * from './zhs';
Loading

0 comments on commit 6e04369

Please sign in to comment.