Skip to content
This repository has been archived by the owner on Jun 3, 2021. It is now read-only.

Commit

Permalink
feat(core): build and pub cea-core
Browse files Browse the repository at this point in the history
  • Loading branch information
beetcb committed May 23, 2021
1 parent 5f6e946 commit 1642e4a
Show file tree
Hide file tree
Showing 36 changed files with 4,075 additions and 434 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
211 changes: 0 additions & 211 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,211 +0,0 @@
<p align="center">
<a href="https://github.com/beetcb/cea">
<img src="https://s4.gifyu.com/images/cea.gif" alt="test" width="600">
</a>

<strong><p align="center"><code>c</code>ampusphere-<code>e</code>legant-<code>a</code>uth</p></strong>

<p align="center">
<a align="center" href="https://www.npmjs.com/package/@beetcb/cea">
<img alt="npm" src="https://img.shields.io/npm/v/@beetcb/cea?style=social">
<img alt="NPM" src="https://img.shields.io/npm/l/@beetcb/cea?style=social">
</a>
</p>
<p align="center">
交互式的配置程序 + 学工系统多用户并发登录 + 云端部署及缓存
<br>
其返回的 <strong>cookie</strong> 可直接用于学工系统或今日校园相关验证
<br>
基于此可以开发更多强大工具集 ( 例如本项目提供的 [今日校园自动签到] 示例 ),欢迎右上角 <a href="https://github.com/beetcb/cea">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/beetcb/cea?style=social">
</a> 支持此项目
<p>
</p>

## Features

- 新增一键部署签到程序:依赖自动安装、触发器自动配置,可能是全网最快、选择最多的部署 👇 (详见 [部署指南](./docs/deploy.md))

- 新增在家签到功能: 在配置学校过程中,可选 `在家签到`,我们会在全国主流城市随机选点(避开高校),也可以自定义地址<details><summary>好奇 `随机` 是哪些地方?</summary>

```js
// Hard coded position info
// Randomly generated from http://api.map.baidu.com/lbsapi
const posGenFromCitys = [
['116.622631', '40.204822', '北京市顺义区X012'],
['115.825701', '32.914915', '安徽省阜阳市颍泉区胜利北路79'],
['119.292590', '26.164789', '福建省福州市晋安区'],
['103.836093', '36.068012', '甘肃省兰州市城关区南滨河东路709'],
['108.360128', '22.883516', '广西壮族自治区南宁市兴宁区'],
['113.391549', '22.590350', '广东省中山市兴港中路172号'],
['111.292396', '30.718343', '湖北省宜昌市西陵区珍珠路32号'],
['118.793117', '32.074771', '江苏省南京市玄武区昆仑路8号'],
]
```

</details>

- 交互式配置: `campusphere-elegant-auth` 提供交互式的命令行完成 用户 及 学校 的配置,同时也支持使用 `toml` 文件来配置

- 验证持久化: 缓存验证信息于内存, 只在失效时登录并更新;云端和本地缓存能力来源于我的另一个项目 [sstore](https://github.com/beetcb/sstore)

- 兼容云服务的 OCR:很多云服务(如云函数)的文件系统并不都是可写入的,我们将 OCR 验证码识别用到的 [tesseract.js](https://github.com/naptha/tesseract.js) 数据包和训练缓存包暂存到了 `/tmp`,降低出错率;同时,为加快国内访问速度,下载节点托管于码云

- 多用户非阻塞: 利用 Node.js 异步特征,多用户可并行,实现毫秒级的多用户同时操作

- 关于签到: (学校配置时)使用百度地图 API 获取学校全局签到地址, 使用今日校园接口返回的签到数据获取签到经纬度, 简单来说, 只需知道学校英文简称即可配置好所有签到信息, 充分懒人化

## Prerequisites

- NPM
- Node.js

### Compatibility

登录地址包含 `iap`(表示已接入今日校园) 字段的实现是统一的,应该没有兼容性问题

若未接入今日校园,只能爬取网页获得凭据:cea 的登录页爬取策略比较智能(并非 `hard coded`),默认根据第一个登录表单完成全部逻辑,这保证了不错的兼容性

如确实遇到了边缘情况,有能力的话可以提交 PR ,只需修改 `./crawler/school-edge-cases.js` 文件,添加你的学校:

```diff
// @ts-check
const schoolEdgeCases = {
+ 学校中文全称: {
+ formIdx: 2, // 默认账号密码登录表单的索引,你需要手动查看 HTML 结构来确定
+ checkCaptchaPath: '/getCaptcha.html', // 检测是否需要验证码的路径
+ getCaptchaPath: '/checkNeedCaptcha.html', // 获取验证码的路径
+ pwdEncrypt: false, // 密码是否加密,默认 true
+ rememberMe: 'on', // [这一项不会影响登录结果]勾选*天免登录后的值,有些学校可能是不同的字符,默认为 true,你需要手动查看登录请求来确定
+ },
}
```

若你不熟悉 Node.js,遇到登录问题,请附带日志提交 [Issue](https://github.com/beetcb/cea/issues/new/choose)

支持使用英文简称的学校列表:[abbrList](./docs/abbrList.sh)

## Get started

1. 安装此项目

```sh
npm i -g @beetcb/cea
```

2. 初始化学校及用户

- 用户配置:

交互式配置用户:

```sh
cea -u
```

或者从 `conf.toml` 文件配置用户,同时也会配置学校

```sh
cea load
```

- 学校配置:

```sh
cea -s
```

- (可选)使用文件配置用户: 根目录下创建 `conf.toml`, 参考以下示例:

```toml
# 文件修改完后仍需执行 `cea load` 加载这些用户,根据提示确保用户已成功加载
# 学校的英文简称(推荐,部分学校支持,请查阅[支持英文简称的学校列表](https://github.com/beetcb/cea/blob/master/docs/abbrList.sh)自行判断)或中文全称(备用选项,所有学校都支持)
school = "whpu"
# 使用学校地址签到
[[users]]
username = "用户名"
password = "密码"
alias = "简称一"
addr = ""
# 使用随机地址在家签到
[[users]]
username = "用户名"
password = "密码"
alias = "简称二"
addr = "home"
# 使用自定义地址在家签到
[[users]]
username = "用户名"
password = "密码"
alias = "简称三"
addr = ["经度", "纬度", "实际地址"]
```

2. 工具使用:
本项目提供 **今日校园自动签到** 示例:执行主程序可自动签到:

```bash
cea sign
```

3. 扩展:

若使用 cea 作为二次开发使用,请配置好学校和用户,然后在你的项目中导入 cea,参考自动签到示例:

```js
const cea = require('@beetcb/cea')
;(async () => {
// Log in and save cookie to cea, using cea.get('cookie') to get them (this function resolve an users array with cookie and sign in methods)
const usersWithTask = await cea.handleCookie()
// Sign in
const logs = await signIn(usersWithTask)
// Print prettier logs info
console.table(logs)
})()
async function signIn(usersWithTask) {
const logs = {}
// sign in asynchronizedly with promise all and diff instance of signApp class
await Promise.all(
usersWithTask.map(async (i) => {
await i.sign.signWithForm()
logs[i.alias || i.id] = i.sign.result
})
)
// store cookie using sstore module
cea.close()
return logs
}
```

使用 `handleCookie` 能够完成登录和 cookie 有效性验证,无需传入任何形参; 再通过 `conf` 可获得 cookie 信息对象,含 `swms``campusphere` 参数,分别对应 学工 和 金智教务(今日校园相关) 验证凭据

4. 清空配置:

```sh
# 清空学校配置
cea rm 'school'
# 清空用户配置
cea rm 'users'
# 清空所有配置
cea rm 'all'
```

## Thanks

登录中加解密过程大量参考 [wisedu-unified-login-api](https://github.com/ZimoLoveShuang/wisedu-unified-login-api) 项目,十分感谢

感谢 [Cloudbase-Framework](https://github.com/Tencent/cloudbase-framework)、[Github Actions](https://github.com/actions)、[Coding CI](https://help.coding.net/docs/ci/intro.html)、[Gitee Pages](https://gitee.com/help/articles/4136) 提供的优秀服务 🎉

## Disclaimer

`@beetcb/cea` - Licensed under [MIT](https://github.com/beetcb/cea/blob/master/LICENSE)

`campusphere-elegant-auth` 仅用于学习和研究 Node.js,请勿商用或违法使用。

> 作者: [<img src="https://img.shields.io/github/followers/beetcb?label=%40beetcb&style=social">](https://github.com/beetcb), 邮箱: `i@beetcb.com`
21 changes: 21 additions & 0 deletions core/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 beet

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1 change: 1 addition & 0 deletions core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
### API Docs:https://beetcb.github.io/cea/api/core
29 changes: 29 additions & 0 deletions core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "cea-core",
"version": "0.0.1",
"description": "basic cea api",
"main": "src/index.js",
"files": ["lib/src/"],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"今日校园",
"campusphere",
"cli",
"cea-cli",
"cea"
],
"author": "github.com/beetcb",
"license": "MIT",
"dependencies": {
"@beetcb/sstore": "^0.1.0",
"@iarna/toml": "^2.2.5",
"@types/node-fetch": "^2.5.10",
"@types/signale": "^1.4.1",
"cheerio": "^1.0.0-rc.9",
"node-fetch": "^2.6.1",
"signale": "^1.4.0",
"tesseract.js": "^2.1.4"
}
}
File renamed without changes.
67 changes: 67 additions & 0 deletions core/src/conf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { UsersConf, SchoolConf } from './types/conf'

import { parse } from '@iarna/toml'
import { resolve } from 'path'
import { StringKV } from './types/helper'

import fetch, { Response } from 'node-fetch'
import log from './utils/logger'
import fs from 'fs'

export function loadConfFromToml(): UsersConf | null {
const path = resolve('./conf.toml')
if (fs.existsSync(path)) {
const usersConf = parse(fs.readFileSync(path, 'utf8'))!.users as UsersConf
return usersConf
}
return null
}

export async function getSchoolInfos(
users: UsersConf
): Promise<SchoolConf | null> {
let res: Response,
defaultAddr = '',
schoolInfos = {} as SchoolConf
const schoolNamesSet = new Set(users.map((e) => e.school))
const isSchoolAddrNeeded = users.find((e) => e.addr.length === 1)
for (const abbreviation of schoolNamesSet) {
res = (await fetch(
`https://mobile.campushoy.com/v6/config/guest/tenant/info?ids=${abbreviation}`
).catch((err) => log.error(err))) as Response

const data = JSON.parse(
(await res.text().catch((err) => log.error(err))) as string
).data[0] as StringKV

let origin = new URL(data.ampUrl).origin
const casOrigin = data.idsUrl

// fall back to ampUrl2 when campusphere not included in the `origin`
if (!origin.includes('campusphere')) {
origin = new URL(data.ampUrl2).origin
}
if (isSchoolAddrNeeded) {
res = (await fetch(
`https://api.map.baidu.com/?qt=s&wd=${encodeURIComponent(
data.name
)}&ak=E4805d16520de693a3fe707cdc962045&rn=10&ie=utf-8&oue=1&fromproduct=jsapi&res=api`
).catch((err) => log.error(err))) as Response
const addrInfo = await res.json()
defaultAddr = addrInfo.content[0].addr
}
schoolInfos[abbreviation] = {
defaultAddr,
loginStartEndpoint: `${origin}/iap/login?service=${encodeURIComponent(
`${origin}/portal/login`
)}`,
swms: casOrigin,
chineseName: data.name,
campusphere: origin,
isIap: data.joinType !== 'NOTCLOUD',
}
log.success({ message: `学校 ${data.name} 默认签到地址:${defaultAddr}` })
log.success({ message: `学校 ${data.name} 已完成设定` })
}
return schoolInfos
}
File renamed without changes.
File renamed without changes.
10 changes: 7 additions & 3 deletions src/index.ts → core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ import log from './utils/logger'
import login from './crawler/login'
import { SchoolConf, UserConfOpts } from './types/conf'

// sstore
export const sstore = require('@beetcb/sstore')

// all the type definations for plugin & cli use
export * from './types/conf'
export * from './types/cookie'
export * from './types/helper'
export * from './campusphere/api'

// methods for configuration
export * from './conf'

//log utils for plugin & cli use
export { log }

export async function handleCookie() {
Expand Down Expand Up @@ -39,5 +45,3 @@ async function handleLogin(i: UserConfOpts, storeCookiePath: string) {
})
}
}

// handleCookie()
File renamed without changes.
File renamed without changes.
6 changes: 2 additions & 4 deletions src/constants.ts → core/src/types/helper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export enum UserAction {
CREATE = '添加用户',
DELETE = '删除用户',
CANCEL = '取消',
export type StringKV = {
[key: string]: string
}

export enum CampusphereEndpoint {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions core/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../tsconfig.base",
"compilerOptions": {
"outDir": "lib/"
}
}
Loading

0 comments on commit 1642e4a

Please sign in to comment.