yz-yuki-plugin 2.0.3-4 → 2.0.3-6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/README.md +2 -2
- package/lib/components/dynamic/Account.js +8 -7
- package/lib/components/dynamic/Content.js +8 -5
- package/lib/components/dynamic/Footer.js +8 -6
- package/lib/components/dynamic/ForwardContent.js +4 -3
- package/lib/components/dynamic/LogoText.js +4 -3
- package/lib/components/dynamic/MainPage.js +4 -3
- package/lib/components/help/Help.js +9 -6
- package/lib/components/loginQrcode/Page.js +4 -3
- package/lib/components/version/Version.js +6 -4
- package/lib/index.js +4 -1
- package/lib/models/bilibili/bilibili.query.d.ts +15 -5
- package/lib/models/bilibili/bilibili.query.js +112 -17
- package/lib/utils/config.d.ts +5 -3
- package/lib/utils/config.js +17 -10
- package/lib/utils/image.d.ts +2 -1
- package/lib/utils/image.js +8 -8
- package/lib/utils/puppeteer.render.d.ts +3 -1
- package/lib/utils/puppeteer.render.js +7 -5
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# 🌰一、安装插件
|
|
12
12
|
|
|
13
13
|
## 选择安装方式
|
|
14
|
-
按照网络情况以及使用的bot框架是`Yunzai-Next`还是`Yunzai-V3
|
|
14
|
+
按照网络情况以及使用的bot框架是`Yunzai-Next`还是`Yunzai-V3`,选择对应的安装方式。
|
|
15
15
|
|
|
16
16
|
### ***(一)Yunzai-Next***
|
|
17
17
|
> 选择其中一种方式安装插件:
|
|
@@ -134,7 +134,7 @@ https://m.weibo.cn/u/7643376782 # 7643376782 为崩坏星穹铁道博主uid
|
|
|
134
134
|
|
|
135
135
|
# 🌈 三、功能列表
|
|
136
136
|
|
|
137
|
-
请使用
|
|
137
|
+
请使用 `#优纪帮助`或 `/yuki帮助` 获取完整帮助
|
|
138
138
|
|
|
139
139
|
- [x] B站动态
|
|
140
140
|
- [x] 微博动态
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import LogoText from './LogoText.js';
|
|
3
|
-
import {
|
|
3
|
+
import { _paths } from '../../utils/paths.js';
|
|
4
|
+
import path from 'path';
|
|
4
5
|
|
|
5
6
|
// Account
|
|
6
7
|
// up账户组件
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
const Bilibililogo = path.join(_paths.pluginResources, 'img/icon/dynamic/bilibili.svg');
|
|
9
|
+
const Weibilogo = path.join(_paths.pluginResources, 'img/icon/dynamic/weibo.svg');
|
|
10
|
+
const AccountCss = path.join(_paths.pluginResources, 'css/dynamic/Account.css');
|
|
10
11
|
const Account = ({ data }) => {
|
|
11
12
|
const renderLogo = (logoSrc, className) => (React.createElement("img", { src: logoSrc, className: className, alt: "logo" }));
|
|
12
13
|
return (React.createElement(React.Fragment, null,
|
|
13
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
14
|
+
React.createElement("link", { rel: "stylesheet", href: AccountCss }),
|
|
14
15
|
React.createElement("div", { className: "account" },
|
|
15
16
|
React.createElement("div", { className: "avatar-container" },
|
|
16
17
|
React.createElement("img", { src: data.face, alt: "\u5934\u50CF", className: "avatar" }),
|
|
@@ -19,8 +20,8 @@ const Account = ({ data }) => {
|
|
|
19
20
|
React.createElement("div", { className: "nickname" }, data.name || ""),
|
|
20
21
|
React.createElement("div", { className: "timestamp" }, data.pubTs || ""))),
|
|
21
22
|
React.createElement("div", { className: "logo-container" },
|
|
22
|
-
data.appName === 'bilibili' && renderLogo(
|
|
23
|
-
data.appName === 'weibo' && renderLogo(
|
|
23
|
+
data.appName === 'bilibili' && renderLogo(Bilibililogo, 'bilibili-logo'),
|
|
24
|
+
data.appName === 'weibo' && renderLogo(Weibilogo, 'weibo-logo'),
|
|
24
25
|
React.createElement(LogoText, { data: data })))));
|
|
25
26
|
};
|
|
26
27
|
var Account$1 = Account;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { _paths } from '../../utils/paths.js';
|
|
3
|
+
import path from 'path';
|
|
3
4
|
|
|
4
5
|
// DynamicContent.tsx
|
|
5
|
-
const
|
|
6
|
+
const ContentBoxGrid4Css = path.join(_paths.pluginResources, 'css/dynamic/Content.box.grid.4.css');
|
|
7
|
+
const ContentBoxGrid9Css = path.join(_paths.pluginResources, 'css/dynamic/Content.box.grid.9.css');
|
|
8
|
+
const ContentCss = path.join(_paths.pluginResources, 'css/dynamic/Content.css');
|
|
6
9
|
const Content = ({ data }) => {
|
|
7
10
|
const picItems = data.pics && (React.createElement("div", { className: 'pic-content' }, data.pics.map((item, index) => {
|
|
8
11
|
if (item) {
|
|
@@ -11,8 +14,8 @@ const Content = ({ data }) => {
|
|
|
11
14
|
}
|
|
12
15
|
return null;
|
|
13
16
|
})));
|
|
14
|
-
const boxGrid_4 = React.createElement("link", { key: "0", rel: "stylesheet", href:
|
|
15
|
-
const boxGrid_9 = React.createElement("link", { key: "0", rel: "stylesheet", href:
|
|
17
|
+
const boxGrid_4 = React.createElement("link", { key: "0", rel: "stylesheet", href: ContentBoxGrid4Css });
|
|
18
|
+
const boxGrid_9 = React.createElement("link", { key: "0", rel: "stylesheet", href: ContentBoxGrid9Css });
|
|
16
19
|
/**动态宫格样式 */
|
|
17
20
|
function getBoxGridStyle(pics) {
|
|
18
21
|
if (!Array.isArray(pics) || pics.length === 0) {
|
|
@@ -66,7 +69,7 @@ const Content = ({ data }) => {
|
|
|
66
69
|
return null;
|
|
67
70
|
}
|
|
68
71
|
const boxGrid = data.boxGrid && (data.pics && getBoxGridStyle(data.pics));
|
|
69
|
-
const contentCss = React.createElement("link", { rel: "stylesheet", href:
|
|
72
|
+
const contentCss = React.createElement("link", { rel: "stylesheet", href: ContentCss });
|
|
70
73
|
switch (data.type) {
|
|
71
74
|
case 'DYNAMIC_TYPE_LIVE_RCMD':
|
|
72
75
|
return (React.createElement(React.Fragment, null,
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ConfigController, BOT_NAME } from 'yunzai';
|
|
3
3
|
import Config from '../../utils/config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { _paths } from '../../utils/paths.js';
|
|
5
|
+
import path from 'path';
|
|
5
6
|
|
|
6
7
|
// Footer.tsx
|
|
7
8
|
const botVersion = ConfigController.package?.version;
|
|
8
|
-
const
|
|
9
|
-
const bilibililogo =
|
|
10
|
-
const weibilogo =
|
|
9
|
+
const yukiPluginVersion = Config.getPackageJsonKey('version', path.join(_paths.pluginPath, 'package.json'));
|
|
10
|
+
const bilibililogo = path.join(_paths.pluginResources, 'img/icon/dynamic/bilibili.svg');
|
|
11
|
+
const weibilogo = path.join(_paths.pluginResources, 'img/icon/dynamic/weibo.svg');
|
|
12
|
+
const FooterCss = path.join(_paths.pluginResources, 'css/dynamic/Footer.css');
|
|
11
13
|
const Footer = ({ data }) => {
|
|
12
14
|
return (React.createElement(React.Fragment, null,
|
|
13
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
15
|
+
React.createElement("link", { rel: "stylesheet", href: FooterCss }),
|
|
14
16
|
React.createElement("div", { className: "footer" },
|
|
15
17
|
React.createElement("div", { className: "footer-text-container" },
|
|
16
18
|
data.appName === 'bilibili' && (React.createElement("svg", { className: 'w-32 h-10 bili-logo-0', style: { width: '8rem', height: '2.5rem' } },
|
|
@@ -29,7 +31,7 @@ const Footer = ({ data }) => {
|
|
|
29
31
|
" & ",
|
|
30
32
|
React.createElement("span", { className: "yuki-plugin-text-title" }, "yuki-plugin"),
|
|
31
33
|
"-v",
|
|
32
|
-
React.createElement("span", { className: "italic" },
|
|
34
|
+
React.createElement("span", { className: "italic" }, yukiPluginVersion))),
|
|
33
35
|
React.createElement("img", { src: data.urlImgData, alt: "\u4E8C\u7EF4\u7801", className: "qr-code" }))));
|
|
34
36
|
};
|
|
35
37
|
var Footer$1 = Footer;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import Account from './Account.js';
|
|
3
3
|
import Content from './Content.js';
|
|
4
|
-
import {
|
|
4
|
+
import { _paths } from '../../utils/paths.js';
|
|
5
|
+
import path from 'path';
|
|
5
6
|
|
|
6
7
|
// ForwardContent
|
|
7
8
|
// 转发动态内容组件
|
|
8
|
-
const
|
|
9
|
+
const ForwardContentCss = path.join(_paths.pluginResources, 'css/dynamic/ForwardContent.css');
|
|
9
10
|
const ForwardContent = ({ data }) => (React.createElement(React.Fragment, null,
|
|
10
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
11
|
+
React.createElement("link", { rel: "stylesheet", href: ForwardContentCss }),
|
|
11
12
|
React.createElement("div", { className: "orig" },
|
|
12
13
|
React.createElement("div", { className: "orig-container", id: "orig-container" },
|
|
13
14
|
React.createElement(Account, { data: data }),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { _paths } from '../../utils/paths.js';
|
|
3
|
+
import path from 'path';
|
|
3
4
|
|
|
4
5
|
// LogoText
|
|
5
6
|
// Logo 文本组件
|
|
6
|
-
const
|
|
7
|
+
const LogoTextCss = path.join(_paths.pluginResources, 'css/dynamic/LogoText.css');
|
|
7
8
|
const LogoText = ({ data }) => (React.createElement(React.Fragment, null,
|
|
8
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
9
|
+
React.createElement("link", { rel: "stylesheet", href: LogoTextCss }),
|
|
9
10
|
data.appName === 'bilibili' && (React.createElement("div", { className: "bilibili-logo-text" }, data.category)),
|
|
10
11
|
data.appName === 'weibo' && (React.createElement("div", { className: "weibo-logo-text" }, data.category))));
|
|
11
12
|
|
|
@@ -3,13 +3,14 @@ import Account from './Account.js';
|
|
|
3
3
|
import Content from './Content.js';
|
|
4
4
|
import ForwardContent from './ForwardContent.js';
|
|
5
5
|
import Footer from './Footer.js';
|
|
6
|
-
import {
|
|
6
|
+
import { _paths } from '../../utils/paths.js';
|
|
7
|
+
import path from 'path';
|
|
7
8
|
|
|
8
9
|
// MainPage.tsx
|
|
9
|
-
const
|
|
10
|
+
const MainPageCss = path.join(_paths.pluginResources, 'css/dynamic/MainPage.css');
|
|
10
11
|
function App({ data }) {
|
|
11
12
|
return (React.createElement(React.Fragment, null,
|
|
12
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
13
|
+
React.createElement("link", { rel: "stylesheet", href: MainPageCss }),
|
|
13
14
|
React.createElement("div", { className: "outside-border" },
|
|
14
15
|
React.createElement("div", { className: "container" },
|
|
15
16
|
React.createElement(Account, { data: data }),
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { BOT_NAME, ConfigController } from 'yunzai';
|
|
3
3
|
import Config from '../../utils/config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { _paths } from '../../utils/paths.js';
|
|
5
|
+
import path from 'path';
|
|
5
6
|
|
|
6
7
|
//help.tsx
|
|
7
8
|
const botVersion = ConfigController.package?.version;
|
|
8
|
-
const
|
|
9
|
+
const yukiPluginVersion = Config.getPackageJsonKey('version', path.join(_paths.pluginPath, 'package.json'));
|
|
10
|
+
const HelpCss = path.join(_paths.pluginResources, 'css/help/help.css');
|
|
11
|
+
const iconPath = (iconName) => path.join(_paths.pluginResources, `img/icon/puplic/${iconName}.png`);
|
|
9
12
|
function App({ data }) {
|
|
10
13
|
return (React.createElement(React.Fragment, null,
|
|
11
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
14
|
+
React.createElement("link", { rel: "stylesheet", href: HelpCss }),
|
|
12
15
|
React.createElement("div", { className: "container", id: "container" },
|
|
13
16
|
React.createElement("div", { className: "head_box" },
|
|
14
17
|
React.createElement("div", { className: "id_text" }, "Yuki-Plugin"),
|
|
15
18
|
React.createElement("h2", { className: "day_text" },
|
|
16
19
|
"\u4F7F\u7528\u8BF4\u660E-v",
|
|
17
|
-
|
|
20
|
+
yukiPluginVersion)),
|
|
18
21
|
data.map((val, index) => (React.createElement("div", { className: "data_box", key: index },
|
|
19
22
|
React.createElement("div", { className: "tab_lable" }, val.group),
|
|
20
23
|
React.createElement("div", { className: "list" }, val.list.map((item, itemIndex) => (React.createElement("div", { className: "item", key: itemIndex },
|
|
21
|
-
React.createElement("img", { className: "icon", src:
|
|
24
|
+
React.createElement("img", { className: "icon", src: iconPath(item.icon), alt: item.title }),
|
|
22
25
|
React.createElement("div", { className: "title" },
|
|
23
26
|
React.createElement("div", { className: "text" }, item.title),
|
|
24
27
|
React.createElement("div", { className: "dec" }, item.desc))))))))),
|
|
@@ -28,7 +31,7 @@ function App({ data }) {
|
|
|
28
31
|
" & ",
|
|
29
32
|
React.createElement("span", { className: "yuki-plugin-text-title" }, "yuki-plugin"),
|
|
30
33
|
"-v",
|
|
31
|
-
React.createElement("span", { className: "italic" },
|
|
34
|
+
React.createElement("span", { className: "italic" }, yukiPluginVersion)))));
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
export { App as default };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { _paths } from '../../utils/paths.js';
|
|
3
|
+
import path from 'path';
|
|
3
4
|
|
|
4
5
|
// QrcodeLoginPage.tsx
|
|
5
|
-
const
|
|
6
|
+
const LoginQrcodeCss = path.join(_paths.pluginResources, 'css/loginQrcode/Page.css');
|
|
6
7
|
function App({ data }) {
|
|
7
8
|
return (React.createElement(React.Fragment, null,
|
|
8
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
9
|
+
React.createElement("link", { rel: "stylesheet", href: LoginQrcodeCss }),
|
|
9
10
|
React.createElement("div", { className: 'container w-96 max-h-96 m-auto text-lg p-5' },
|
|
10
11
|
React.createElement("div", { className: "txt-0 text-center mt-3 mb-3 p-1 text-blue-500" },
|
|
11
12
|
"Created By yuki-plugin",
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { BOT_NAME, ConfigController } from 'yunzai';
|
|
3
3
|
import Config from '../../utils/config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { _paths } from '../../utils/paths.js';
|
|
5
|
+
import path from 'path';
|
|
5
6
|
|
|
6
7
|
const botVersion = ConfigController.package?.version;
|
|
7
|
-
const
|
|
8
|
+
const yukiPluginVersion = Config.getPackageJsonKey('version', path.join(_paths.pluginPath, 'package.json'));
|
|
9
|
+
const VersionCss = path.join(_paths.pluginResources, 'css/version/version.css');
|
|
8
10
|
function App({ data }) {
|
|
9
11
|
return (React.createElement(React.Fragment, null,
|
|
10
|
-
React.createElement("link", { rel: "stylesheet", href:
|
|
12
|
+
React.createElement("link", { rel: "stylesheet", href: VersionCss }),
|
|
11
13
|
React.createElement("div", { className: "container", id: "container" },
|
|
12
14
|
data.map((item, idx) => (React.createElement("div", { key: idx, className: "version-card" },
|
|
13
15
|
React.createElement("div", { className: "title" },
|
|
@@ -21,7 +23,7 @@ function App({ data }) {
|
|
|
21
23
|
" & ",
|
|
22
24
|
React.createElement("span", { className: "yuki-plugin-text-title" }, "yuki-plugin"),
|
|
23
25
|
"-v",
|
|
24
|
-
React.createElement("span", { className: "italic" },
|
|
26
|
+
React.createElement("span", { className: "italic" }, yukiPluginVersion)))));
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export { App as default };
|
package/lib/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { applicationOptions, setBotTask, useEvent } from 'yunzai';
|
|
3
3
|
import Config from './utils/config.js';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { _paths } from './utils/paths.js';
|
|
4
6
|
import * as index$1 from './apps/index.js';
|
|
5
7
|
import { BiliTask } from './models/bilibili/bilibili.task.js';
|
|
6
8
|
import { WeiboTask } from './models/weibo/weibo.task.js';
|
|
7
9
|
|
|
10
|
+
const yukiPluginVersion = Config.getPackageJsonKey('version', path.join(_paths.pluginPath, 'package.json'));
|
|
8
11
|
let biliConfigData = Config.getConfigData("config", "bilibili", "config");
|
|
9
12
|
let weiboConfigData = Config.getConfigData("config", "weibo", "config");
|
|
10
13
|
/** B站动态任务 函数 */
|
|
@@ -35,7 +38,7 @@ var index = () => {
|
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
logger.info(chalk.rgb(0, 190, 255)(`-----------------------------------------`));
|
|
38
|
-
logger.info(chalk.rgb(255, 225, 255)(`优纪插件 ${
|
|
41
|
+
logger.info(chalk.rgb(255, 225, 255)(`优纪插件 ${yukiPluginVersion} 初始化~`));
|
|
39
42
|
logger.info(chalk.rgb(255, 245, 255)(`作者:snowtafir`));
|
|
40
43
|
logger.info(chalk.rgb(255, 225, 255)(`仓库地址:`));
|
|
41
44
|
logger.info(chalk.rgb(255, 245, 255)(`https://github.com/snowtafir/yuki-plugin`));
|
|
@@ -17,12 +17,22 @@ export declare class BiliQuery {
|
|
|
17
17
|
*/
|
|
18
18
|
static parseRichTextNodes: (nodes: any[] | string | any) => any;
|
|
19
19
|
/**获取完整B站文章内容
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
* @param postUrl - 文章链接: https://www.bilibili.com/read/cvxxxx 或者 https://www.bilibili.com/opus/xxxx
|
|
21
|
+
* @returns {Json} 完整的B站文章内容json数据
|
|
22
22
|
*/
|
|
23
|
-
static getFullArticleContent(postUrl: string): Promise<
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
static getFullArticleContent(postUrl: string): Promise<{
|
|
24
|
+
readInfo: any;
|
|
25
|
+
articleType: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**解析旧版完整文章内容 */
|
|
28
|
+
static praseFullOldTypeArticleContent(content: string): string;
|
|
29
|
+
/**解析新版完整文章内容
|
|
30
|
+
* @param paragraphs - MODULE_TYPE_CONTENT 类型文章的段落数组
|
|
31
|
+
*/
|
|
32
|
+
static praseFullNewTypeArticleContent: (paragraphs: any[] | any) => {
|
|
33
|
+
content: string;
|
|
34
|
+
img: any[];
|
|
35
|
+
};
|
|
26
36
|
static formatUrl(url: string): string;
|
|
27
37
|
/**
|
|
28
38
|
* 生成动态消息文字内容
|
|
@@ -86,16 +86,31 @@ class BiliQuery {
|
|
|
86
86
|
pics = desc?.pics;
|
|
87
87
|
pics = pics.map((item) => { return { url: item?.url, width: item?.width, height: item?.height }; }) || [];
|
|
88
88
|
formatData.data.title = desc?.title;
|
|
89
|
+
// 文章内容过长,则尝试获取全文
|
|
89
90
|
if ((desc?.summary?.text)?.length >= 480) {
|
|
90
|
-
const readInfo = await this.getFullArticleContent(this.formatUrl(desc?.jump_url));
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
formatData.data.content
|
|
95
|
-
|
|
91
|
+
const { readInfo, articleType } = await this.getFullArticleContent(this.formatUrl(desc?.jump_url));
|
|
92
|
+
// 文章链接类型为 cv(旧类型) 或者 opus(新类型)
|
|
93
|
+
if (articleType === "cv") {
|
|
94
|
+
formatData.data.content = this.praseFullOldTypeArticleContent(readInfo?.content);
|
|
95
|
+
if (String(formatData.data.content).length < 100) {
|
|
96
|
+
formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text) || "";
|
|
97
|
+
formatData.data.pics = pics;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
formatData.data.pics = [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (articleType === "opus") {
|
|
104
|
+
const { content, img } = this.praseFullNewTypeArticleContent(readInfo?.paragraphs);
|
|
105
|
+
formatData.data.content = content;
|
|
106
|
+
formatData.data.pics = (img && img.length > 0) ? img : pics;
|
|
107
|
+
if (content && content.length < 100) {
|
|
108
|
+
formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text);
|
|
109
|
+
}
|
|
96
110
|
}
|
|
97
111
|
else {
|
|
98
|
-
formatData.data.
|
|
112
|
+
formatData.data.content = this.parseRichTextNodes(desc?.summary?.rich_text_nodes || desc?.summary?.text) || "";
|
|
113
|
+
formatData.data.pics = pics;
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
else {
|
|
@@ -106,7 +121,7 @@ class BiliQuery {
|
|
|
106
121
|
else if (majorType === "MAJOR_TYPE_ARTICLE") {
|
|
107
122
|
desc = data?.modules?.module_dynamic?.major?.article || {};
|
|
108
123
|
pics = desc?.covers;
|
|
109
|
-
pics = pics.map((item) => {
|
|
124
|
+
pics = pics.map((item) => ({ url: item }));
|
|
110
125
|
formatData.data.title = desc?.title;
|
|
111
126
|
formatData.data.content = this.parseRichTextNodes(desc?.desc);
|
|
112
127
|
}
|
|
@@ -210,8 +225,8 @@ class BiliQuery {
|
|
|
210
225
|
}
|
|
211
226
|
};
|
|
212
227
|
/**获取完整B站文章内容
|
|
213
|
-
|
|
214
|
-
|
|
228
|
+
* @param postUrl - 文章链接: https://www.bilibili.com/read/cvxxxx 或者 https://www.bilibili.com/opus/xxxx
|
|
229
|
+
* @returns {Json} 完整的B站文章内容json数据
|
|
215
230
|
*/
|
|
216
231
|
static async getFullArticleContent(postUrl) {
|
|
217
232
|
const Cookie = await readSyncCookie();
|
|
@@ -221,11 +236,24 @@ class BiliQuery {
|
|
|
221
236
|
responseType: 'text'
|
|
222
237
|
});
|
|
223
238
|
const text = response.data;
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
239
|
+
let match, readInfo, articleType;
|
|
240
|
+
if (/^https:\/\/www.bilibili.com\/read\/cv/.test(postUrl)) {
|
|
241
|
+
match = String(text).match(/"readInfo":([\s\S]+?),"readViewInfo":/);
|
|
242
|
+
if (match) {
|
|
243
|
+
const full_json_text = match[1];
|
|
244
|
+
readInfo = JSON.parse(full_json_text);
|
|
245
|
+
articleType = 'cv';
|
|
246
|
+
return { readInfo, articleType };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else if (/^https:\/\/www.bilibili.com\/opus\//.test(postUrl)) {
|
|
250
|
+
match = String(text).match(/"module_content":([\s\S]+?),\s*"module_type":"MODULE_TYPE_CONTENT"/);
|
|
251
|
+
if (match) {
|
|
252
|
+
const full_json_text = match[1];
|
|
253
|
+
readInfo = JSON.parse(full_json_text);
|
|
254
|
+
articleType = 'opus';
|
|
255
|
+
return { readInfo, articleType };
|
|
256
|
+
}
|
|
229
257
|
}
|
|
230
258
|
}
|
|
231
259
|
catch (err) {
|
|
@@ -233,8 +261,8 @@ class BiliQuery {
|
|
|
233
261
|
return null;
|
|
234
262
|
}
|
|
235
263
|
}
|
|
236
|
-
|
|
237
|
-
static
|
|
264
|
+
/**解析旧版完整文章内容 */
|
|
265
|
+
static praseFullOldTypeArticleContent(content) {
|
|
238
266
|
content = String(content).replace(/\n/g, '<br>');
|
|
239
267
|
// 使用正则表达式匹配 <img> 标签的 data-src 属性
|
|
240
268
|
const imgTagRegex = /<img[^>]*data-src="([^"]*)"[^>]*>/g;
|
|
@@ -245,6 +273,73 @@ class BiliQuery {
|
|
|
245
273
|
});
|
|
246
274
|
return content;
|
|
247
275
|
}
|
|
276
|
+
/**解析新版完整文章内容
|
|
277
|
+
* @param paragraphs - MODULE_TYPE_CONTENT 类型文章的段落数组
|
|
278
|
+
*/
|
|
279
|
+
static praseFullNewTypeArticleContent = (paragraphs) => {
|
|
280
|
+
if (Array.isArray(paragraphs)) {
|
|
281
|
+
// 筛选出正文和图片
|
|
282
|
+
paragraphs = paragraphs.filter(paragraph => paragraph.para_type === 1 || paragraph.para_type === 2);
|
|
283
|
+
let content = "";
|
|
284
|
+
let img = [];
|
|
285
|
+
paragraphs.forEach((item) => {
|
|
286
|
+
switch (item.para_type) {
|
|
287
|
+
case 1:
|
|
288
|
+
// 处理正文
|
|
289
|
+
content += item.text.nodes.map((node) => {
|
|
290
|
+
let nodeType = node.type;
|
|
291
|
+
if (nodeType === 'TEXT_NODE_TYPE_RICH') {
|
|
292
|
+
let richType = node?.rich?.type;
|
|
293
|
+
switch (richType) {
|
|
294
|
+
case 'RICH_TEXT_NODE_TYPE_TOPIC':
|
|
295
|
+
// 确保链接以 https:// 开头
|
|
296
|
+
let jumpUrl = node?.rich?.jump_url;
|
|
297
|
+
if (jumpUrl && !jumpUrl.startsWith('http://') && !jumpUrl.startsWith('https://')) {
|
|
298
|
+
jumpUrl = `https://${jumpUrl}`;
|
|
299
|
+
}
|
|
300
|
+
return `<span class="bili-rich-text-module topic" href="${jumpUrl}">${node?.rich?.text}</span>`;
|
|
301
|
+
case 'RICH_TEXT_NODE_TYPE_TEXT':
|
|
302
|
+
// 正文将 \n 替换为 <br> 以实现换行
|
|
303
|
+
return node?.rich?.text.replace(/\n/g, '<br>');
|
|
304
|
+
case 'RICH_TEXT_NODE_TYPE_AT':
|
|
305
|
+
// 处理 @ 类型,使用官方的HTML标签写法
|
|
306
|
+
return `<span data-module="desc" data-type="at" data-oid="${node?.rich?.rid}" class="bili-rich-text-module at">${node?.rich?.text}</span>`;
|
|
307
|
+
case 'RICH_TEXT_NODE_TYPE_LOTTERY':
|
|
308
|
+
// 处理互动抽奖类型,使用官方的HTML标签写法
|
|
309
|
+
return `<span data-module="desc" data-type="lottery" data-oid="${node?.rich?.rid}" class="bili-rich-text-module lottery">${node?.rich?.text}</span>`;
|
|
310
|
+
case 'RICH_TEXT_NODE_TYPE_WEB':
|
|
311
|
+
// 处理 RICH_TEXT_NODE_TYPE_WEB 类型,直接拼接 text 属性
|
|
312
|
+
return node?.rich?.text;
|
|
313
|
+
case 'RICH_TEXT_NODE_TYPE_EMOJI':
|
|
314
|
+
// 处理表情类型,使用 img 标签显示表情
|
|
315
|
+
const emoji = node?.rich?.emoji;
|
|
316
|
+
return `<img src="${emoji?.icon_url}" alt="${emoji?.text}" title="${emoji?.text}" style="vertical-align: middle; width: ${emoji?.size}em; height: ${emoji?.size}em;">`;
|
|
317
|
+
case 'RICH_TEXT_NODE_TYPE_GOODS':
|
|
318
|
+
// 处理商品推广类型,使用官方的HTML标签写法
|
|
319
|
+
const goods_url = node?.rich?.jump_url;
|
|
320
|
+
return `<span data-module="desc" data-type="goods" data-url="${goods_url}" data-oid="${node?.rich?.rid}" class="bili-rich-text-module goods ${node?.rich?.icon_name}">​${node?.rich?.text}</span>`;
|
|
321
|
+
default:
|
|
322
|
+
return node;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else if (nodeType === "TEXT_NODE_TYPE_WORD") {
|
|
326
|
+
return `${node?.word?.words}<br>`;
|
|
327
|
+
}
|
|
328
|
+
}).join('');
|
|
329
|
+
break;
|
|
330
|
+
case 2:
|
|
331
|
+
// 处理图片
|
|
332
|
+
img = img.concat(item?.pic?.pics.map((item) => { return { url: item?.url, width: item?.width, height: item?.height }; }) || []);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
return { content, img };
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// 未知类型,返回null
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
248
343
|
// 处理斜杠开头的链接
|
|
249
344
|
static formatUrl(url) {
|
|
250
345
|
return 0 == url.indexOf('//') ? `https:${url}` : url;
|
package/lib/utils/config.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import chokidar from "chokidar";
|
|
|
3
3
|
* Config 类用于管理配置文件的读取和监听
|
|
4
4
|
*/
|
|
5
5
|
declare class Config {
|
|
6
|
-
readonly packageJsonPath: string;
|
|
7
6
|
readonly defaultConfigPath: string;
|
|
8
7
|
readonly userConfigPath: string;
|
|
9
8
|
defaultConfig: Record<string, any>;
|
|
@@ -64,8 +63,11 @@ declare class Config {
|
|
|
64
63
|
* @param value 配置项的值
|
|
65
64
|
*/
|
|
66
65
|
updateConfigItem(appDir: string, functionName: string, key: string, value: any): void;
|
|
67
|
-
/** 读取package.json
|
|
68
|
-
|
|
66
|
+
/** 读取package.json文件,获取指定key的值
|
|
67
|
+
* @param keyName 要获取的key名称
|
|
68
|
+
* @param path package.json文件路径
|
|
69
|
+
*/
|
|
70
|
+
getPackageJsonKey(keyName: string, path: string): string | null;
|
|
69
71
|
}
|
|
70
72
|
declare const _default: Config;
|
|
71
73
|
export default _default;
|
package/lib/utils/config.js
CHANGED
|
@@ -9,14 +9,12 @@ import { _paths } from './paths.js';
|
|
|
9
9
|
* Config 类用于管理配置文件的读取和监听
|
|
10
10
|
*/
|
|
11
11
|
class Config {
|
|
12
|
-
packageJsonPath;
|
|
13
12
|
defaultConfigPath;
|
|
14
13
|
userConfigPath;
|
|
15
14
|
defaultConfig;
|
|
16
15
|
userConfig;
|
|
17
16
|
watcher;
|
|
18
17
|
constructor() {
|
|
19
|
-
this.packageJsonPath = path.join(_paths.pluginPath, 'package.json');
|
|
20
18
|
/** 默认设置 */
|
|
21
19
|
this.defaultConfigPath = path.join(_paths.pluginPath, 'defaultConfig');
|
|
22
20
|
this.defaultConfig = {};
|
|
@@ -160,15 +158,24 @@ class Config {
|
|
|
160
158
|
config[key] = value; // 更新配置项
|
|
161
159
|
this.saveConfig("config", appDir, functionName, config); // 保存更新后的配置
|
|
162
160
|
}
|
|
163
|
-
/** 读取package.json
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
161
|
+
/** 读取package.json文件,获取指定key的值
|
|
162
|
+
* @param keyName 要获取的key名称
|
|
163
|
+
* @param path package.json文件路径
|
|
164
|
+
*/
|
|
165
|
+
getPackageJsonKey(keyName, path) {
|
|
166
|
+
try {
|
|
167
|
+
const content = fs.readFileSync(path, 'utf-8');
|
|
168
|
+
const packageJson = JSON.parse(content);
|
|
169
|
+
const match = packageJson[keyName];
|
|
170
|
+
if (match) {
|
|
171
|
+
return match;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
170
176
|
}
|
|
171
|
-
|
|
177
|
+
catch (error) {
|
|
178
|
+
logger.error(`getPackageJsonKey error: ${error}`);
|
|
172
179
|
return null;
|
|
173
180
|
}
|
|
174
181
|
}
|
package/lib/utils/image.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Picture, ComponentCreateOpsionType } from 'react-puppeteer';
|
|
2
2
|
import { ScreenshotOptions } from '@/utils/puppeteer.render';
|
|
3
3
|
export declare class Image extends Picture {
|
|
4
|
+
private yukiPuppeteerRender;
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* 整合截图方法
|
|
6
7
|
*/
|
|
7
8
|
constructor();
|
|
8
9
|
/**
|
package/lib/utils/image.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Picture } from 'react-puppeteer';
|
|
3
3
|
import { YukiPuppeteerRender } from './puppeteer.render.js';
|
|
4
4
|
import * as index from '../components/index.js';
|
|
5
5
|
|
|
6
|
-
// 初始化 组件渲染对象
|
|
7
|
-
const com = new Component();
|
|
8
|
-
const yukiPuppeteerRender = new YukiPuppeteerRender();
|
|
9
6
|
class Image extends Picture {
|
|
7
|
+
yukiPuppeteerRender;
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
9
|
+
* 整合截图方法
|
|
12
10
|
*/
|
|
13
11
|
constructor() {
|
|
14
12
|
// 继承实例
|
|
15
13
|
super();
|
|
16
|
-
//
|
|
17
|
-
this.
|
|
14
|
+
// 组件渲染对象
|
|
15
|
+
this.Com;
|
|
16
|
+
// start puppeteer
|
|
18
17
|
this.Pup.start();
|
|
18
|
+
this.yukiPuppeteerRender = new YukiPuppeteerRender(this.Pup);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* @param uid 唯一标识符
|
|
@@ -27,7 +27,7 @@ class Image extends Picture {
|
|
|
27
27
|
*/
|
|
28
28
|
async renderPage(uid, page, props = {}, ScreenshotOptions, ComponentCreateOpsion) {
|
|
29
29
|
const Page = index[page];
|
|
30
|
-
return yukiPuppeteerRender.yukiScreenshot(
|
|
30
|
+
return this.yukiPuppeteerRender.yukiScreenshot(this.Com.compile({
|
|
31
31
|
join_dir: page,
|
|
32
32
|
html_name: `${uid}.html`,
|
|
33
33
|
html_body: React.createElement(Page, { ...props }),
|
|
@@ -16,7 +16,9 @@ export type ScreenshotOptions = {
|
|
|
16
16
|
modelName?: string;
|
|
17
17
|
saveHtmlfile?: boolean;
|
|
18
18
|
};
|
|
19
|
-
export declare class YukiPuppeteerRender
|
|
19
|
+
export declare class YukiPuppeteerRender {
|
|
20
|
+
private puppeteerInstance;
|
|
21
|
+
constructor(puppeteerInstance: Puppeteer);
|
|
20
22
|
/**
|
|
21
23
|
* 截图并返回buffer
|
|
22
24
|
* @param htmlPath 绝对路径
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Puppeteer } from 'react-puppeteer';
|
|
2
1
|
import fs__default from 'fs';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { _paths } from './paths.js';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
class YukiPuppeteerRender {
|
|
6
|
+
puppeteerInstance;
|
|
7
|
+
constructor(puppeteerInstance) {
|
|
8
|
+
this.puppeteerInstance = puppeteerInstance;
|
|
9
|
+
}
|
|
8
10
|
/**
|
|
9
11
|
* 截图并返回buffer
|
|
10
12
|
* @param htmlPath 绝对路径
|
|
@@ -21,12 +23,12 @@ class YukiPuppeteerRender extends Puppeteer {
|
|
|
21
23
|
* @returns {false | {img: buffer[]}}
|
|
22
24
|
*/
|
|
23
25
|
async yukiScreenshot(htmlPath, Options) {
|
|
24
|
-
if (!(await this.isStart()))
|
|
26
|
+
if (!(await this.puppeteerInstance.isStart()))
|
|
25
27
|
return false;
|
|
26
28
|
let name = Options?.modelName ?? 'yuki-plugin';
|
|
27
29
|
let pageHeight = Options?.pageSplitHeight ?? 8000; // 分割图片高度,默认 8000
|
|
28
30
|
try {
|
|
29
|
-
const page = await this.browser?.newPage().catch(err => {
|
|
31
|
+
const page = await this.puppeteerInstance.browser?.newPage().catch(err => {
|
|
30
32
|
logger.error(err);
|
|
31
33
|
});
|
|
32
34
|
if (!page)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yz-yuki-plugin",
|
|
3
|
-
"version": "2.0.3-
|
|
3
|
+
"version": "2.0.3-6",
|
|
4
4
|
"description": "优纪插件,yunzaijs 关于 微博推送、B站推送 等功能的拓展插件",
|
|
5
5
|
"author": "snowtafir",
|
|
6
6
|
"type": "module",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"md5": "^2.3.0",
|
|
31
31
|
"moment": "^2.30.1",
|
|
32
32
|
"node-fetch": "^3.3.2",
|
|
33
|
-
"puppeteer": "^
|
|
33
|
+
"puppeteer": "^23.3.0",
|
|
34
34
|
"qrcode": "^1.5.4",
|
|
35
35
|
"react": "^18.3.1",
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
|
-
"react-puppeteer": "^1.0.
|
|
37
|
+
"react-puppeteer": "^1.0.7-rc.0",
|
|
38
38
|
"redis": "^4.7.0",
|
|
39
|
-
"yaml": "^2.5.
|
|
39
|
+
"yaml": "^2.5.1",
|
|
40
40
|
"yarn": "^1.19.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
"node-fetch": "^3.3.2",
|
|
73
73
|
"nodemon": "^3.1.4",
|
|
74
74
|
"prettier": "^3.3.3",
|
|
75
|
-
"puppeteer": "^
|
|
75
|
+
"puppeteer": "^23.3.0",
|
|
76
76
|
"qrcode": "^1.5.4",
|
|
77
77
|
"react": "^18.3.1",
|
|
78
78
|
"react-dom": "^18.3.1",
|
|
79
|
-
"react-puppeteer": "1.0.
|
|
79
|
+
"react-puppeteer": "^1.0.7-rc.0",
|
|
80
80
|
"redis": "^4.7.0",
|
|
81
81
|
"rollup": "^4.20.0",
|
|
82
82
|
"rollup-plugin-auto-external": "^2.0.0",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"ts-node": "^10.9.2",
|
|
88
88
|
"tsx": "^4.19.0",
|
|
89
89
|
"typescript": "^5.5.4",
|
|
90
|
-
"yunzai": "^1.1.
|
|
91
|
-
"yunzai-mys": "^1.0.
|
|
90
|
+
"yunzai": "^1.1.4",
|
|
91
|
+
"yunzai-mys": "^1.0.6"
|
|
92
92
|
},
|
|
93
93
|
"files": [
|
|
94
94
|
"public",
|