Skip to content

Commit b9d332a

Browse files
committed
refactor: 重构整个项目
1 parent 6fd0c15 commit b9d332a

25 files changed

+990
-1016
lines changed

eslint.config.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import js from "@eslint/js";
2+
import globals from "globals";
3+
import tseslint from "typescript-eslint";
4+
import prettier from "eslint-config-prettier";
5+
import eslintPluginPrettier from "eslint-plugin-prettier";
6+
import { defineConfig } from "eslint/config";
7+
8+
export default defineConfig([
9+
{
10+
ignores: ["lib/**"],
11+
},
12+
{
13+
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
14+
plugins: { js, prettier: eslintPluginPrettier },
15+
extends: ["js/recommended", prettier],
16+
rules: { "prettier/prettier": ["error", { semi: true }] },
17+
languageOptions: { globals: globals.node },
18+
},
19+
tseslint.configs.recommended,
20+
]);

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,30 @@
3333
"numbro": "^2.5.0"
3434
},
3535
"devDependencies": {
36+
"@eslint/js": "^9.27.0",
3637
"@types/handlebars": "^4.1.0",
3738
"@types/numbro": "^1.9.3",
39+
"eslint": "^9.27.0",
40+
"eslint-config-prettier": "^10.1.5",
41+
"eslint-plugin-prettier": "^5.4.0",
42+
"globals": "^16.1.0",
43+
"prettier": "^3.5.3",
3844
"typescript": "^5.8.3",
45+
"typescript-eslint": "^8.32.1",
3946
"yml-register": "^1.2.5"
4047
},
4148
"koishi": {
4249
"description": {
4350
"zh": "一个能够解析 BiliBili 链接的 Koishi 插件。"
4451
},
52+
"service": {
53+
"required": [
54+
"http"
55+
],
56+
"optional": [
57+
"puppeteer"
58+
]
59+
},
4560
"locales": [
4661
"zh"
4762
]

readme.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ When regex match `bilibili\.com\/opus\/(\d+)`, reply
116116
当正则表达式符合 `bilibili\.com\/opus\/(\d+)`,回复
117117

118118
```
119-
{{modules.module_author.name}}的动态
120-
<img src=\"{{modules.module_dynamic.additional.goods.items.[0].cover}}\" />
121-
{{modules.module_dynamic.desc.text}}
122-
转发:{{formatNumber modules.module_stat.forward.count}} | 评论:{{formatNumber modules.module_stat.comment.count}} | 点赞:{{formatNumber modules.module_stat.like.count}}
119+
{{item.modules.module_author.name}}的动态
120+
<img src=\"{{item.modules.module_dynamic.additional.goods.items.[0].cover}}\" />
121+
{{item.modules.module_dynamic.desc.text}}
122+
转发:{{formatNumber item.modules.module_stat.forward.count}} | 评论:{{formatNumber item.modules.module_stat.comment.count}} | 点赞:{{formatNumber item.modules.module_stat.like.count}}
123123
```
124124

125125
### Space/空间

src/api/bili/article.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Dict } from "koishi";
2+
3+
import { BiliAPI } from ".";
4+
import { runtime } from "../..";
5+
6+
/**
7+
* 获取专栏基本信息
8+
* @param id 专栏 ID
9+
* @returns API 内容
10+
*/
11+
export async function fetch_api(id: string): Promise<BiliAPI<Dict>> {
12+
const ret: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
13+
`https://api.bilibili.com/x/article/viewinfo?id=${id.replace(/^cv/, "")}`,
14+
{
15+
headers: {
16+
Host: "api.bilibili.com",
17+
"User-Agent": runtime.config.userAgent,
18+
},
19+
},
20+
);
21+
return ret;
22+
}
23+
24+
export async function puppeteer_fetch_api(id: string) {
25+
if (!runtime.ctx.puppeteer)
26+
throw new Error("Please enable puppeteer service.");
27+
28+
const browser = runtime.ctx.puppeteer.browser;
29+
const context = await browser.createBrowserContext();
30+
const page = await context.newPage();
31+
if (runtime.config.userAgent) {
32+
await page.setUserAgent(runtime.config.userAgent);
33+
}
34+
35+
let ret: BiliAPI<Dict>;
36+
try {
37+
await page.goto("https://www.bilibili.com/404", {
38+
waitUntil: "networkidle2",
39+
});
40+
await page.goto(
41+
`https://api.bilibili.com/x/article/viewinfo?id=${id.replace(/^cv/, "")}`,
42+
{ waitUntil: "networkidle2" },
43+
);
44+
45+
ret = await page.evaluate(() => {
46+
return JSON.parse(document.body.innerText);
47+
});
48+
} finally {
49+
await page.close();
50+
await context.close();
51+
}
52+
53+
return ret;
54+
}

src/api/bili/audio.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Dict } from "koishi";
2+
3+
import { BiliAPI } from ".";
4+
import { runtime } from "../..";
5+
6+
/**
7+
* 获取音乐基本信息
8+
* @param id 音乐 ID
9+
* @returns API 内容
10+
*/
11+
export async function fetch_api(id: string): Promise<BiliAPI<Dict>> {
12+
const ret: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
13+
`https://www.bilibili.com/audio/music-service-c/web/song/info?sid=${id}`,
14+
{
15+
headers: {
16+
Host: "www.bilibili.com",
17+
"User-Agent": runtime.config.userAgent,
18+
},
19+
},
20+
);
21+
return ret;
22+
}
23+
24+
/**
25+
* 获取歌单基本信息
26+
* @param id 歌单 ID
27+
* @returns API 内容
28+
*/
29+
export async function fetch_am_api(id: string): Promise<BiliAPI<Dict>> {
30+
const ret: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
31+
`https://www.bilibili.com/audio/music-service-c/web/menu/info?sid=${id}`,
32+
{
33+
headers: {
34+
Host: "www.bilibili.com",
35+
"User-Agent": runtime.config.userAgent,
36+
},
37+
},
38+
);
39+
return ret;
40+
}

src/api/bili/bangumi.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { Dict } from "koishi";
2+
3+
import { logger, runtime } from "../..";
4+
import { BiliAPI } from ".";
5+
6+
interface BGMType {
7+
type: string;
8+
id: string;
9+
}
10+
11+
/**
12+
* 解析 ID 类型
13+
* @param id 番剧集 ID
14+
* @returns type: ID 类型, id: 番剧集 ID
15+
*/
16+
function bgm_type_parse(id: string): BGMType {
17+
const idRegex = [
18+
{ pattern: /ep([0-9]+)/i, type: "ep" },
19+
{ pattern: /ss([0-9]+)/i, type: "ss" },
20+
];
21+
22+
let ret: BGMType;
23+
24+
for (const rule of idRegex) {
25+
const match = id.match(rule.pattern);
26+
if (match) {
27+
ret = {
28+
type: rule.type,
29+
id: match[1],
30+
};
31+
}
32+
}
33+
34+
return ret;
35+
}
36+
37+
/**
38+
* 获取番剧集基本信息
39+
* @param id 番剧集 ID
40+
* @returns API 内容
41+
*/
42+
export async function fetch_web_api(id: string): Promise<BiliAPI<Dict>> {
43+
let url: string;
44+
const bgm = bgm_type_parse(id);
45+
46+
switch (bgm.type) {
47+
case "ep":
48+
url = `https://api.bilibili.com/pgc/view/web/season?ep_id=${bgm.id}`;
49+
break;
50+
case "ss":
51+
url = `https://api.bilibili.com/pgc/view/web/season?season_id=${bgm.id}`;
52+
break;
53+
default:
54+
throw new Error(`No such bangumi type: ${bgm.type}`);
55+
}
56+
57+
logger.debug(url);
58+
const ret = await runtime.ctx.http.get<BiliAPI<Dict>>(url, {
59+
headers: {
60+
Host: "api.bilibili.com",
61+
"User-Agent": runtime.config.userAgent,
62+
},
63+
});
64+
65+
ret.data = ret.result;
66+
return ret;
67+
}
68+
69+
/**
70+
* 获取番剧集基本信息
71+
* @param id 番剧集 ID
72+
* @returns API 内容
73+
*/
74+
export async function fetch_mdid_api(id: string): Promise<BiliAPI<Dict>> {
75+
const mdInfo: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
76+
`https://api.bilibili.com/pgc/review/user?media_id=${id.replace(/^md/, "")}`,
77+
{
78+
headers: {
79+
Host: "api.bilibili.com",
80+
"User-Agent": runtime.config.userAgent,
81+
},
82+
},
83+
);
84+
85+
if (!mdInfo.result) {
86+
throw new Error("Fetch bangumi infomation via mdid failed!");
87+
}
88+
89+
const ret: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
90+
`https://api.bilibili.com/pgc/view/web/season?season_id=${mdInfo.result.media.season_id}`,
91+
{
92+
headers: {
93+
Host: "api.bilibili.com",
94+
"User-Agent": runtime.config.userAgent,
95+
},
96+
},
97+
);
98+
ret.data = ret.result;
99+
return ret;
100+
}

src/api/bili/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface BiliAPI<T> {
2+
code: number;
3+
message?: string;
4+
msg?: string;
5+
ttl?: number;
6+
data?: T;
7+
result?: T;
8+
}

src/api/bili/live.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Dict } from "koishi";
2+
3+
import { BiliAPI } from ".";
4+
import { runtime } from "../..";
5+
6+
/**
7+
* 解析直播状态
8+
* @param statusCode 状态代码
9+
* @returns 状态文字
10+
*/
11+
export function getStatusText(statusCode: number) {
12+
switch (statusCode) {
13+
case 0:
14+
return "未开播";
15+
case 1:
16+
return "直播中";
17+
case 2:
18+
return "轮播中";
19+
default:
20+
return "未知状态";
21+
}
22+
}
23+
24+
/**
25+
* 获取直播间基本信息
26+
* @param id 直播 ID
27+
* @returns API 内容
28+
*/
29+
export async function fetch_api(id: string): Promise<BiliAPI<Dict>> {
30+
const ret: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
31+
`https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${id}`,
32+
{
33+
headers: {
34+
Host: "api.live.bilibili.com",
35+
"User-Agent": runtime.config.userAgent,
36+
},
37+
},
38+
);
39+
return ret;
40+
}

src/api/bili/opus.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Dict } from "koishi";
2+
3+
import { BiliAPI } from ".";
4+
import { runtime } from "../..";
5+
6+
/**
7+
* 获取动态详情
8+
* @param id 动态 ID
9+
* @returns API 内容
10+
*/
11+
export async function fetch_api(id: string): Promise<BiliAPI<Dict>> {
12+
const ret: BiliAPI<Dict> = await runtime.ctx.http.get<BiliAPI<Dict>>(
13+
`https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${id}`,
14+
{
15+
headers: {
16+
Host: "api.bilibili.com",
17+
"User-Agent": runtime.config.userAgent,
18+
},
19+
},
20+
);
21+
return ret;
22+
}

src/api/bili/short.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { runtime } from "../..";
2+
3+
/**
4+
* 根据短链接重定向获取正常链接
5+
* @param id 短链接 ID
6+
* @returns 正常链接
7+
*/
8+
export async function get_redir_url(id: string) {
9+
const data = await runtime.ctx.http.get(`https://b23.tv/${id}`, {
10+
redirect: "manual",
11+
headers: {
12+
"User-Agent": runtime.config.userAgent,
13+
},
14+
});
15+
16+
try {
17+
const match = data.match(/<a\s+(?:[^>]*?\s+)?href="([^"]*)"/i);
18+
return match[1];
19+
} catch {
20+
return null;
21+
}
22+
}

0 commit comments

Comments
 (0)