Skip to content

Commit

Permalink
docs: 补充规则例子
Browse files Browse the repository at this point in the history
  • Loading branch information
aooiuu committed Sep 16, 2024
1 parent d0b36e7 commit 29d6f88
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 97 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ jobs:
run: sed -i 's/\/usr\/bin\/env node/node/g' C:/Users/runneradmin/setup-pnpm/node_modules/.pnpm/pnpm@9.4.0/node_modules/pnpm/bin/pnpm.cjs
shell: bash

- run: npx changelogithub
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - run: npx changelogithub
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- run: pnpm install
- run: pnpm run build
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default withMermaid({
nav: [
{ text: '指南', link: '/desktop/' },
{ text: '规则', link: '/rule/' },
{ text: '规则测试', link: '/play/' }
{ text: '在线工具', link: '/play/' }
],
search: {
provider: 'local'
Expand Down
7 changes: 6 additions & 1 deletion docs/play/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
> 开发中, 目前规则部分不支持 `@filter` `@js`, 暂不支持解析流程测试
# 取文本测试

> 开发中, 目前在线工具规则部分不支持 `@filter` `@js`, 暂不支持解析流程测试
<RulePlay />

---

<!--@include: ../rule/expression.md-->
23 changes: 23 additions & 0 deletions docs/rule/expression.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
| 特性 | 说明 | 示例 |
| ---------- | :---------------------------------------- | :--------------------------------- |
| `@css` | 使用 css 选择器查找内容 [规则测试][@css] | `@css:.box1 .box2@text` |
| `@json` | 使用 jsonpath 查找内容 [规则测试][@json] | `@json:$.list[:1].title` |
| `@xpath` | 使用 xpath 查找内容 [规则测试][@xpath] | `@xpath://*[@class="box3"]/text()` |
| `@js` | 使用 js 脚本 | `@js:1+1` |
| `@filter` | 模拟浏览器加载地址后匹配指定链接 | `@filter:(?:m3u8\|mp4)` |
| `@replace` | 替换匹配到的内容为空 [规则测试][@replace] | `@replace:\d` |
| `##` | 正则替换 [规则测试][##] | `$.a##2##替换文本` |
| `{‍​‍{}}` | 拼接 [规则测试][​拼接] | `http://www.aaa.com/{‍{$.id}}` |
| `\|\|` | 或, 直到规则匹配成功 [规则测试][] | `$.a\|\|$.b` |
| `&&` | | |
| 嵌套组合 | | `$.info.body@css:.box1 .box2@text` |

规则可以省略开头的,**@css****@xpath****@json**, 因为解析器会尝试自动识别。

[@css]: /play/?_t=1#eyJpbnB1dFRleHQiOiI8ZGl2IGNsYXNzPVwiYm94MVwiPjxkaXYgY2xhc3M9XCJib3gyXCI+Y29udGVudDwvZGl2PjwvZGl2PiIsInJ1bGUiOiIuYm94MSAuYm94MkB0ZXh0IiwiaXNMaXN0IjpmYWxzZX0=
[@json]: /play/?_t=2#eyJpbnB1dFRleHQiOiJ7XCJhXCI6IDF9IiwicnVsZSI6IiQuYSIsImlzTGlzdCI6ZmFsc2V9
[@xpath]: /play/?_t=3#eyJpbnB1dFRleHQiOiI8ZGl2IGNsYXNzPVwiYm94MVwiPjxkaXYgY2xhc3M9XCJib3gyXCI+Y29udGVudDI8L2Rpdj48ZGl2IGNsYXNzPVwiYm94M1wiPmNvbnRlbnQzPC9kaXY+PC9kaXY+IiwicnVsZSI6Ii8vKltAY2xhc3M9XCJib3gzXCJdL3RleHQoKSIsImlzTGlzdCI6ZmFsc2V9
[@replace]: /play/?_t=6#eyJpbnB1dFRleHQiOiJhMTJiM2MiLCJydWxlIjoiQHJlcGxhY2U6XFxkIiwiaXNMaXN0IjpmYWxzZX0=
[##]: /play/?_t=7#eyJpbnB1dFRleHQiOiJ7XCJhXCI6IFwiMTIzXCJ9IiwicnVsZSI6IiQuYSMjMiMj5pu/5o2i5paH5pysIiwiaXNMaXN0IjpmYWxzZX0=
[​拼接]: /play/?_t=8#eyJpbnB1dFRleHQiOiJ7XCJhXCI6IDF9IiwicnVsZSI6InF7eyQueHx8JC5hfX13IiwiaXNMaXN0IjpmYWxzZX0=
[]: /play/?_t=9#eyJpbnB1dFRleHQiOiJ7XCJhXCI6IFwiMVwifSIsInJ1bGUiOiIkLmJ8fCQuYSIsImlzTGlzdCI6ZmFsc2V9
134 changes: 54 additions & 80 deletions docs/rule/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,6 @@ outline: deep

::: code-group

```json
{
"id": "xxx-xxx-xxx-xxx-xxx",
"author": "",
"name": "",
"host": "",
"icon": "",
"contentType": 1,
"sort": 0,
"userAgent": "",
"enableDiscover": false,
"discoverUrl": "",
"discoverNextUrl": "",
"discoverList": "",
"discoverTags": "",
"discoverName": "",
"discoverCover": "",
"discoverChapter": "",
"discoverDescription": "",
"discoverResult": "",
"enableSearch": false,
"searchUrl": "",
"searchAuthor": "",
"chapterCover": "",
"chapterTime": "",
"discoverAuthor": "",
"searchList": "",
"searchTags": "",
"searchName": "",
"searchCover": "",
"searchChapter": "",
"searchDescription": "",
"searchResult": "",
"enableMultiRoads": false,
"chapterRoads": "",
"chapterRoadName": "",
"chapterUrl": "",
"chapterNextUrl": "",
"chapterList": "",
"chapterName": "",
"chapterResult": "",
"contentUrl": "",
"contentNextUrl": "",
"contentItems": ""
}
```

```typescript
export interface Rule {
// ===== 通用字段 =====
Expand Down Expand Up @@ -142,12 +95,59 @@ enum ContentType {
}
```

```json
{
"id": "xxx-xxx-xxx-xxx-xxx",
"author": "",
"name": "",
"host": "",
"icon": "",
"contentType": 1,
"sort": 0,
"userAgent": "",
"enableDiscover": false,
"discoverUrl": "",
"discoverNextUrl": "",
"discoverList": "",
"discoverTags": "",
"discoverName": "",
"discoverCover": "",
"discoverChapter": "",
"discoverDescription": "",
"discoverResult": "",
"enableSearch": false,
"searchUrl": "",
"searchAuthor": "",
"chapterCover": "",
"chapterTime": "",
"discoverAuthor": "",
"searchList": "",
"searchTags": "",
"searchName": "",
"searchCover": "",
"searchChapter": "",
"searchDescription": "",
"searchResult": "",
"enableMultiRoads": false,
"chapterRoads": "",
"chapterRoadName": "",
"chapterUrl": "",
"chapterNextUrl": "",
"chapterList": "",
"chapterName": "",
"chapterResult": "",
"contentUrl": "",
"contentNextUrl": "",
"contentItems": ""
}
```

:::

> 格式 `eso://:xxxxx` 是压缩后的规则, 软件也会自动识别, 也可以使用 [在线规则编解码工具](/play/comparess) 还原成json
>
> 并不是每个字段都是必填的, 按需填写既可。
:::

## 规则字段类型

### URL地址规则
Expand Down Expand Up @@ -405,39 +405,13 @@ Content-Type: application/json

> `discoverUrl` 虽然规则看起来像 `URL地址规则`, 但是用法截然不同, 所以这里单独说明
## 规则支持情况

- ✅ 理论支持
- ⚠️ 支持部分
- ❌ 暂不支持
## 规则表达式

### URL地址规则
[在线练习](/play/)

| 特性 | 支持情况 | 示例 |
| ---- | :------: | ---------------------------------------------------------------------------------------------------------- |
| URL || `https://xxx.com/search?q=$keyword&pageSize=10` |
| JSON || `{"url":"https://xxx.com/search","method":"post","headers":{"token":"111"},"body":{"keyword":"$keyword"}}` |
| @js || `@js:(() => { return {url, method, body, headers}; })();` |

### 规则表达式

| 特性 | 支持情况 | 说明 | 示例 |
| ---------- | :------: | -------------------------------- | --------------------------------------- |
| `@css` || | `@css:.box1 .box2@text` |
| `@json` || | `@json:$.list[:1].title` |
| `@xpath` || | `@xpath://*[@class="box3"]/text()` |
| `@js` || | |
| `@filter` || 模拟浏览器加载地址后匹配指定链接 | `@filter:(?:m3u8\|mp4)(?:$\|/\|\\?\|&)` |
| `@replace` || | `@replace:.*?url=\|.*?v=` |
| `##` || 正则替换 | `@css:.c2 a@href##\\d+\\.html` |
| `{‍​‍{}}` || 使用变量 | `http://www.aaa.com/{‍{$.id}}` |
| 嵌套组合 || | `$.info.body@css:.box1 .box2@text` |
| `\|\|` || | |
| `&&` || | |

规则可以省略开头的,**@css****@xpath****@json**, 因为解析器会尝试自动识别。
<!--@include: ./expression.md-->

## 规则表达式
---

### CSS

Expand Down Expand Up @@ -474,7 +448,7 @@ Content-Type: application/json
>
> 如果上一个流程`结果规则`拿到的结果是 `456`, 那么在`取列表规则`字段中 `@js:lastResult` 将输出 `456`, 在`URL地址规则`字段中 `@js:result` 将输出 `456`
内置方法: `CryptoJS``fetch``xpath`
内置方法: `CryptoJS``fetch``xpath``cheerio`

#### CryptoJS

Expand Down
30 changes: 30 additions & 0 deletions packages/core/__test__/analyzer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,36 @@ describe('analyzer', () => {
expect(content).toEqual('iPhone');
});

it('|| getString', async () => {
const body = '{"a": "1"}';
const content = await analyzerManager.getString('$.b||$.a', body);
expect(content).toEqual('1');
});

it('|| getStringList', async () => {
const body = '{"a": "1"}';
const content = await analyzerManager.getStringList('$.b||$.a', body);
expect(content).toEqual(['1']);
});

it('##', async () => {
const body = '{"a": "123"}';
const content = await analyzerManager.getString('$.a##2##替换文本', body);
expect(content).toEqual('1替换文本3');
});

it('{{}}', async () => {
const body = '{"a": 1}';
const content = await analyzerManager.getString('q{{$.x||$.a}}w', body);
expect(content).toEqual('q1w');
});

it('@replace', async () => {
const body = '123';
const content = await analyzerManager.getString('@replace:2', body);
expect(content).toEqual('13');
});

it('XPath', async () => {
const body = '<div class="box1"><div class="box2">content2</div><div class="box3">content3</div></div>';
const content = await analyzerManager.getString('//*[@class="box3"]/text()', body);
Expand Down
96 changes: 84 additions & 12 deletions packages/core/src/analyzer/AnalyzerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,54 @@ export class AnalyzerManager {
}

async _getString(r: SingleRule, rule?: string): Promise<string> {
const _rule = rule || r.rule;
try {
const res = await r.analyzer.getString(_rule);
return Array.isArray(res) ? res.join('').trim() : res;
} catch (error: any) {
throw new AnalyzerException(error?.message);
if (rule === undefined) {
rule = r.rule;
}

if (r.analyzer instanceof AnalyzerJS) {
const temp = await r.analyzer.getString(rule);
if (temp === null) {
return '';
} else if (Array.isArray(temp)) {
return temp
.map((s) => typeof s !== 'undefined' && String(s).trim())
.filter((s) => s)
.join(' ');
}
return String(temp).trim();
}

let result = '';

if (rule.includes('&&')) {
const rs = [];
for (const rSimple of rule.split('&&')) {
const temp = await this._getString(r, rSimple);
if (temp.length > 0) {
rs.push(temp);
}
}
return rs.join(' ');
} else if (rule.includes('||')) {
for (const rSimple of rule.split('||')) {
const temp = await this._getString(r, rSimple);
if (temp.length > 0) {
return temp;
}
}
} else {
const temp = await r.analyzer.getString(rule);
if (Array.isArray(temp)) {
result = temp
.map((s) => typeof s !== 'undefined' && String(s).trim())
.filter((s) => s)
.join(' ');
} else if (temp !== null) {
result = String(temp).trim();
}
}

return result.length === 0 ? '' : this.replaceSmart(r.replace)(result);
}

public async getString(rule: string, body: string): Promise<string> {
Expand Down Expand Up @@ -197,13 +238,44 @@ export class AnalyzerManager {
return temp;
}

async _getStringList(r: SingleRule, rule?: string): Promise<string[]> {
const _rule = rule || r.rule;
try {
return await r.analyzer.getStringList(rule || r.rule);
} catch (error: any) {
throw new AnalyzerException(error?.message);
async _getStringList(r: SingleRule, rule?: string): Promise<string[] | string> {
if (rule === undefined) {
rule = r.rule;
}

if (r.analyzer instanceof AnalyzerJS) {
return await r.analyzer.getStringList(rule);
}

if (rule.includes('&&')) {
const result = [];
for (const rSimple of rule.split('&&')) {
const temp = await this._getStringList(r, rSimple);
if (Array.isArray(temp)) {
result.push(...temp.map((s) => s?.trim()).filter((s) => s));
} else if (temp && temp.trim()) {
result.push(temp.trim());
}
}
return result;
} else if (rule.includes('||')) {
let result: string[] = [];
for (const rSimple of rule.split('||')) {
const temp = await this._getStringList(r, rSimple);
if (Array.isArray(temp)) {
result = temp.map((s) => s?.trim()).filter((s) => s);
} else if (temp && temp.trim()) {
result = [temp.trim()];
}
if (result.length > 0) {
return result;
}
}
} else {
return await r.analyzer.getStringList(rule);
}

return [];
}

public async getStringList(rule: string, body: string): Promise<string[]> {
Expand Down

0 comments on commit 29d6f88

Please sign in to comment.