@easyasstudio/xhs-cli 1.4.3
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/AGENTS.md +29 -0
- package/LICENSE +641 -0
- package/README.md +108 -0
- package/dist/browser/cookie.d.ts +1 -0
- package/dist/browser/cookie.d.ts.map +1 -0
- package/dist/browser/cookie.js +2 -0
- package/dist/browser/cookie.js.map +1 -0
- package/dist/browser/index.d.ts +7 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +201 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/cli/cliRouter.d.ts +6 -0
- package/dist/cli/cliRouter.d.ts.map +1 -0
- package/dist/cli/cliRouter.js +249 -0
- package/dist/cli/cliRouter.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +13 -0
- package/dist/cli/parseArgs.d.ts.map +1 -0
- package/dist/cli/parseArgs.js +64 -0
- package/dist/cli/parseArgs.js.map +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +67 -0
- package/dist/config.js.map +1 -0
- package/dist/toolset/accountRegistry.d.ts +33 -0
- package/dist/toolset/accountRegistry.d.ts.map +1 -0
- package/dist/toolset/accountRegistry.js +198 -0
- package/dist/toolset/accountRegistry.js.map +1 -0
- package/dist/toolset/cachePaths.d.ts +3 -0
- package/dist/toolset/cachePaths.d.ts.map +1 -0
- package/dist/toolset/cachePaths.js +7 -0
- package/dist/toolset/cachePaths.js.map +1 -0
- package/dist/toolset/get_metrics.d.ts +47 -0
- package/dist/toolset/get_metrics.d.ts.map +1 -0
- package/dist/toolset/get_metrics.js +201 -0
- package/dist/toolset/get_metrics.js.map +1 -0
- package/dist/toolset/get_note_detail.d.ts +3 -0
- package/dist/toolset/get_note_detail.d.ts.map +1 -0
- package/dist/toolset/get_note_detail.js +100 -0
- package/dist/toolset/get_note_detail.js.map +1 -0
- package/dist/toolset/get_profile.d.ts +16 -0
- package/dist/toolset/get_profile.d.ts.map +1 -0
- package/dist/toolset/get_profile.js +79 -0
- package/dist/toolset/get_profile.js.map +1 -0
- package/dist/toolset/get_recent_posts.d.ts +3 -0
- package/dist/toolset/get_recent_posts.d.ts.map +1 -0
- package/dist/toolset/get_recent_posts.js +113 -0
- package/dist/toolset/get_recent_posts.js.map +1 -0
- package/dist/toolset/index.d.ts +12 -0
- package/dist/toolset/index.d.ts.map +1 -0
- package/dist/toolset/index.js +48 -0
- package/dist/toolset/index.js.map +1 -0
- package/dist/toolset/login.d.ts +4 -0
- package/dist/toolset/login.d.ts.map +1 -0
- package/dist/toolset/login.js +194 -0
- package/dist/toolset/login.js.map +1 -0
- package/dist/toolset/post.d.ts +27 -0
- package/dist/toolset/post.d.ts.map +1 -0
- package/dist/toolset/post.js +148 -0
- package/dist/toolset/post.js.map +1 -0
- package/dist/toolset/postValidate.d.ts +8 -0
- package/dist/toolset/postValidate.d.ts.map +1 -0
- package/dist/toolset/postValidate.js +59 -0
- package/dist/toolset/postValidate.js.map +1 -0
- package/dist/toolset/publishButton.d.ts +7 -0
- package/dist/toolset/publishButton.d.ts.map +1 -0
- package/dist/toolset/publishButton.js +28 -0
- package/dist/toolset/publishButton.js.map +1 -0
- package/dist/toolset/publishedRecords.d.ts +15 -0
- package/dist/toolset/publishedRecords.d.ts.map +1 -0
- package/dist/toolset/publishedRecords.js +60 -0
- package/dist/toolset/publishedRecords.js.map +1 -0
- package/dist/toolset/sessionResolve.d.ts +12 -0
- package/dist/toolset/sessionResolve.d.ts.map +1 -0
- package/dist/toolset/sessionResolve.js +39 -0
- package/dist/toolset/sessionResolve.js.map +1 -0
- package/dist/toolset/sessionTypes.d.ts +13 -0
- package/dist/toolset/sessionTypes.d.ts.map +1 -0
- package/dist/toolset/sessionTypes.js +3 -0
- package/dist/toolset/sessionTypes.js.map +1 -0
- package/dist/utils/cache.d.ts +10 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +124 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/titleToFilename.d.ts +2 -0
- package/dist/utils/titleToFilename.d.ts.map +1 -0
- package/dist/utils/titleToFilename.js +19 -0
- package/dist/utils/titleToFilename.js.map +1 -0
- package/dist/utils/version_check.d.ts +2 -0
- package/dist/utils/version_check.d.ts.map +1 -0
- package/dist/utils/version_check.js +105 -0
- package/dist/utils/version_check.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/toolset/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAqB,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGzE,SAAS,gBAAgB,CAAC,OAAyB;IACjD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,cAAc,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB;IACvD,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACtD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,WAAW,qBAAqB,CAAC,WAAW,CAAC,EAAE,CAAC;IACzD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAyB;IAClE,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAyB,EACzB,OAAyB;IAEzB,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,cAAc,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,OAAyB;IAEzB,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAkB;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;IACxE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/toolset/login.ts"],"names":[],"mappings":"AAKA,OAAO,EAAuC,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAoGzF,iBAAe,KAAK,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAqFtE;AAID,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// 使用 puppeteer-core 实现小红书登录
|
|
2
|
+
import { launchBrowser } from '../browser/index.js';
|
|
3
|
+
import { getUserProfile, validateUserProfile } from './get_profile.js';
|
|
4
|
+
// 等待登录完成
|
|
5
|
+
async function waitForLogin(page, timeout = 180000) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
let lastCheckUrl = page.url();
|
|
8
|
+
const navigationPromises = [];
|
|
9
|
+
const navigationHandler = () => {
|
|
10
|
+
const promise = page.waitForNavigation({
|
|
11
|
+
waitUntil: 'domcontentloaded',
|
|
12
|
+
timeout: 5000,
|
|
13
|
+
}).catch(() => null);
|
|
14
|
+
navigationPromises.push(promise);
|
|
15
|
+
};
|
|
16
|
+
page.on('framenavigated', navigationHandler);
|
|
17
|
+
try {
|
|
18
|
+
while (Date.now() - startTime < timeout) {
|
|
19
|
+
try {
|
|
20
|
+
// 检查浏览器和页面是否已断开连接
|
|
21
|
+
if (page.isClosed() || !page.browser().isConnected()) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
await Promise.race([
|
|
25
|
+
...navigationPromises,
|
|
26
|
+
new Promise(resolve => setTimeout(resolve, 2000)),
|
|
27
|
+
]);
|
|
28
|
+
// 清空已完成的导航 Promise
|
|
29
|
+
navigationPromises.length = 0;
|
|
30
|
+
// 等待页面稳定(网络请求完成)
|
|
31
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
32
|
+
// 再次检查连接状态
|
|
33
|
+
if (page.isClosed() || !page.browser().isConnected()) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// 检查当前页面URL
|
|
37
|
+
const currentUrl = page.url();
|
|
38
|
+
// 如果URL发生变化,说明可能发生了跳转(比如登录成功后的重定向)
|
|
39
|
+
if (currentUrl !== lastCheckUrl) {
|
|
40
|
+
lastCheckUrl = currentUrl;
|
|
41
|
+
// 等待页面完全加载(等待网络请求完成)
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
43
|
+
// 再次检查连接状态
|
|
44
|
+
if (page.isClosed() || !page.browser().isConnected()) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
// 如果当前不在登录页面,且在小红书域名下,尝试使用轻量方式检测
|
|
48
|
+
const isLoginPage = currentUrl.includes('/login') || currentUrl.includes('/signin');
|
|
49
|
+
if (!isLoginPage && currentUrl.includes('xiaohongshu.com')) {
|
|
50
|
+
// 等待页面元素加载完成
|
|
51
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
52
|
+
// 使用 fetch 方式检测,不重新加载页面,避免刷新
|
|
53
|
+
const canAccessCreator = await page.evaluate(async () => {
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch('https://creator.xiaohongshu.com/new/home', {
|
|
56
|
+
method: 'HEAD',
|
|
57
|
+
redirect: 'manual',
|
|
58
|
+
});
|
|
59
|
+
// 如果返回 200,说明可以访问(已登录)
|
|
60
|
+
// 如果返回 302/301 等重定向,需要检查 Location header
|
|
61
|
+
if (response.status === 200) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (response.status >= 300 && response.status < 400) {
|
|
65
|
+
const location = response.headers.get('location') || '';
|
|
66
|
+
// 如果重定向到登录页面,说明未登录
|
|
67
|
+
return !location.includes('/login') && !location.includes('/signin');
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// 如果能访问创作者中心,说明已登录
|
|
76
|
+
if (canAccessCreator) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// URL 没有变化,继续等待
|
|
82
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
// 检查是否是浏览器关闭导致的错误
|
|
86
|
+
if (e instanceof Error && (e.message.includes('Target closed') || e.message.includes('Session closed') || e.message.includes('Protocol error'))) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
// 如果访问出错,可能是网络问题,继续等待
|
|
90
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
page.off('framenavigated', navigationHandler);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// 主登录函数(userDataDir 可选:多账号时传入 ~/.xhs-cli/.cache/accounts/<slug>/browser-data)
|
|
100
|
+
async function login(userDataDir) {
|
|
101
|
+
let browser = null;
|
|
102
|
+
try {
|
|
103
|
+
// 登录时使用非无头模式,让用户可以看到并操作
|
|
104
|
+
// 添加登录时需要的额外参数
|
|
105
|
+
const loginExtraArgs = [
|
|
106
|
+
'--disable-accelerated-2d-canvas',
|
|
107
|
+
'--disable-software-rasterizer',
|
|
108
|
+
'--disable-extensions',
|
|
109
|
+
'--disable-background-networking',
|
|
110
|
+
'--disable-background-timer-throttling',
|
|
111
|
+
'--disable-backgrounding-occluded-windows',
|
|
112
|
+
'--disable-renderer-backgrounding',
|
|
113
|
+
'--disable-features=TranslateUI',
|
|
114
|
+
'--disable-ipc-flooding-protection',
|
|
115
|
+
'--disable-sync',
|
|
116
|
+
'--disable-default-apps',
|
|
117
|
+
'--no-first-run',
|
|
118
|
+
'--no-default-browser-check',
|
|
119
|
+
'--disable-infobars',
|
|
120
|
+
];
|
|
121
|
+
browser = await launchBrowser(false, loginExtraArgs, userDataDir);
|
|
122
|
+
const page = await browser.newPage();
|
|
123
|
+
await page.goto('https://creator.xiaohongshu.com/new/home', {
|
|
124
|
+
waitUntil: 'domcontentloaded',
|
|
125
|
+
timeout: 30000,
|
|
126
|
+
});
|
|
127
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
128
|
+
// 检查页面是否已关闭
|
|
129
|
+
if (page.isClosed() || !page.browser().isConnected()) {
|
|
130
|
+
console.error('❌ 浏览器已关闭,登录中断\n');
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const currentUrl = page.url();
|
|
134
|
+
const isLoginPage = currentUrl.includes('/login') || currentUrl.includes('/signin');
|
|
135
|
+
if (!isLoginPage && currentUrl.includes('creator.xiaohongshu.com')) {
|
|
136
|
+
// 再次检查页面连接状态
|
|
137
|
+
if (page.isClosed() || !page.browser().isConnected()) {
|
|
138
|
+
console.error('❌ 浏览器已关闭,登录中断\n');
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const userProfile = await getUserProfile(page);
|
|
142
|
+
if (!validateUserProfile(userProfile)) {
|
|
143
|
+
throw new Error('获取用户资料失败:返回的数据无效');
|
|
144
|
+
}
|
|
145
|
+
return userProfile;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.error('⏰ 您有 120 秒时间完成登录\n');
|
|
149
|
+
const loginSuccess = await waitForLogin(page, 120000);
|
|
150
|
+
if (loginSuccess) {
|
|
151
|
+
// 检查页面连接状态
|
|
152
|
+
if (page.isClosed() || !page.browser().isConnected()) {
|
|
153
|
+
console.error('❌ 浏览器已关闭,登录中断\n');
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const userProfile = await getUserProfile(page);
|
|
157
|
+
return userProfile;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.log('❌ 登录超时或失败\n');
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// 检查是否是浏览器关闭导致的错误
|
|
167
|
+
if (error instanceof Error && (error.message.includes('Target closed') || error.message.includes('Session closed') || error.message.includes('Protocol error'))) {
|
|
168
|
+
console.error('❌ 浏览器已关闭,登录中断\n');
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.error('❌ 登录过程出错:', error);
|
|
172
|
+
if (error instanceof Error) {
|
|
173
|
+
console.error('错误信息:', error.message);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
if (browser) {
|
|
180
|
+
try {
|
|
181
|
+
// 检查浏览器是否已连接,避免重复关闭导致的错误
|
|
182
|
+
if (browser.isConnected()) {
|
|
183
|
+
await browser.close();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
// 忽略关闭浏览器时的错误(可能已经被用户关闭)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// 导出登录函数
|
|
193
|
+
export { login };
|
|
194
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/toolset/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type { PostNoteParams } from './postValidate.js';
|
|
2
|
+
/** 发布流程结果(不含平台 noteId) */
|
|
3
|
+
export interface PostNoteResult {
|
|
4
|
+
success: boolean;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export type PostNoteArgs = {
|
|
8
|
+
/** 标题(必填) */
|
|
9
|
+
title: string;
|
|
10
|
+
/** 正文 */
|
|
11
|
+
content: string;
|
|
12
|
+
/** 本地图片路径,顺序即上传顺序,1~18 张 */
|
|
13
|
+
imagePaths: string[];
|
|
14
|
+
/**
|
|
15
|
+
* 为 true 时在填表后自动点击页面「发布」按钮;默认 false,仅填表并提示在浏览器中手动发布或修改。
|
|
16
|
+
*/
|
|
17
|
+
publish?: boolean;
|
|
18
|
+
/** 为 true 时使用无头 Chrome;也可用环境变量 XHS_HEADLESS=1 */
|
|
19
|
+
headless?: boolean;
|
|
20
|
+
/** 可选:多账号会话 Chrome userDataDir */
|
|
21
|
+
browserUserDataDir?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 打开发布页并填入标题、正文与图片;不读写队列目录。
|
|
25
|
+
*/
|
|
26
|
+
export declare function postNote(args: PostNoteArgs): Promise<PostNoteResult>;
|
|
27
|
+
//# sourceMappingURL=post.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post.d.ts","sourceRoot":"","sources":["../../src/toolset/post.ts"],"names":[],"mappings":"AAeA,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,0BAA0B;AAC1B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AA6CF;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAwG1E"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// 发布小红书笔记(无队列:每次调用仅使用传入的标题、正文与本地图片路径)
|
|
2
|
+
import { launchBrowser } from '../browser/index.js';
|
|
3
|
+
import { ensureAppDataLayout } from '../config.js';
|
|
4
|
+
import { PUBLISH_BUTTON_LABELS, PUBLISH_FOOTER_SELECTORS, } from './publishButton.js';
|
|
5
|
+
import { validateImagePaths, validatePostParams, } from './postValidate.js';
|
|
6
|
+
/** 等待并点击 .publish-page-publish-btn 内的红色「发布」按钮(不扫描全页,避免误点) */
|
|
7
|
+
async function waitAndClickPublish(page) {
|
|
8
|
+
const selectors = [...PUBLISH_FOOTER_SELECTORS];
|
|
9
|
+
const labels = [...PUBLISH_BUTTON_LABELS];
|
|
10
|
+
const isReady = (sels, allowed) => {
|
|
11
|
+
const norm = (s) => s.replace(/\s+/g, '');
|
|
12
|
+
for (const sel of sels) {
|
|
13
|
+
for (const b of Array.from(document.querySelectorAll(sel))) {
|
|
14
|
+
const el = b;
|
|
15
|
+
if (el.disabled)
|
|
16
|
+
continue;
|
|
17
|
+
if (allowed.includes(norm(el.textContent ?? '')))
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
await page.waitForFunction(isReady, { timeout: 45000 }, selectors, labels);
|
|
24
|
+
const clicked = await page.evaluate((sels, allowed) => {
|
|
25
|
+
const norm = (s) => s.replace(/\s+/g, '');
|
|
26
|
+
for (const sel of sels) {
|
|
27
|
+
for (const b of Array.from(document.querySelectorAll(sel))) {
|
|
28
|
+
const el = b;
|
|
29
|
+
if (el.disabled)
|
|
30
|
+
continue;
|
|
31
|
+
if (!allowed.includes(norm(el.textContent ?? '')))
|
|
32
|
+
continue;
|
|
33
|
+
el.click();
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}, selectors, labels);
|
|
39
|
+
if (!clicked) {
|
|
40
|
+
throw new Error('未在 .publish-page-publish-btn 内找到可点击的「发布」按钮');
|
|
41
|
+
}
|
|
42
|
+
await new Promise((r) => setTimeout(r, 2500));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 打开发布页并填入标题、正文与图片;不读写队列目录。
|
|
46
|
+
*/
|
|
47
|
+
export async function postNote(args) {
|
|
48
|
+
ensureAppDataLayout();
|
|
49
|
+
const title = args.title.trim();
|
|
50
|
+
if (!title) {
|
|
51
|
+
throw new Error('标题不能为空');
|
|
52
|
+
}
|
|
53
|
+
const params = { title, content: args.content };
|
|
54
|
+
validatePostParams(params);
|
|
55
|
+
validateImagePaths(args.imagePaths);
|
|
56
|
+
const imagePaths = [...args.imagePaths];
|
|
57
|
+
const autoPublish = args.publish === true;
|
|
58
|
+
const headless = args.headless === true ||
|
|
59
|
+
process.env.XHS_HEADLESS === '1' ||
|
|
60
|
+
process.env.XHS_HEADLESS?.toLowerCase() === 'true';
|
|
61
|
+
const browser = await launchBrowser(headless, [], args.browserUserDataDir);
|
|
62
|
+
try {
|
|
63
|
+
const page = await browser.newPage();
|
|
64
|
+
await page.goto('https://creator.xiaohongshu.com/new/home', {
|
|
65
|
+
waitUntil: 'domcontentloaded',
|
|
66
|
+
timeout: 30000,
|
|
67
|
+
});
|
|
68
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
69
|
+
const currentUrl = page.url();
|
|
70
|
+
const isLoginPage = currentUrl.includes('/login') || currentUrl.includes('/signin');
|
|
71
|
+
if (isLoginPage) {
|
|
72
|
+
throw new Error('未登录状态。请先运行 xhs login 进行登录。');
|
|
73
|
+
}
|
|
74
|
+
await page.goto('https://creator.xiaohongshu.com/publish/publish?from=homepage&target=image', {
|
|
75
|
+
waitUntil: 'domcontentloaded',
|
|
76
|
+
timeout: 30000,
|
|
77
|
+
});
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
79
|
+
try {
|
|
80
|
+
await page.waitForSelector('input.upload-input[type="file"]', { timeout: 10000 });
|
|
81
|
+
const uploadInput = await page.$('input.upload-input[type="file"]');
|
|
82
|
+
if (!uploadInput) {
|
|
83
|
+
throw new Error('未找到图片上传输入框');
|
|
84
|
+
}
|
|
85
|
+
await uploadInput.uploadFile(...imagePaths);
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await page.waitForSelector('input.d-text', { timeout: 5000 });
|
|
93
|
+
const titleInput = await page.$('input.d-text');
|
|
94
|
+
if (titleInput) {
|
|
95
|
+
await titleInput.click({ clickCount: 3 });
|
|
96
|
+
await titleInput.type(params.title, { delay: 100 });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// 标题可选失败,继续填正文
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await page.waitForSelector('div.tiptap.ProseMirror[contenteditable="true"]', { timeout: 5000 });
|
|
104
|
+
const contentSet = await page.evaluate((content) => {
|
|
105
|
+
const editor = document.querySelector('div.tiptap.ProseMirror[contenteditable="true"]');
|
|
106
|
+
if (!editor)
|
|
107
|
+
return false;
|
|
108
|
+
editor.focus();
|
|
109
|
+
editor.innerHTML = '';
|
|
110
|
+
const textNode = document.createTextNode(content);
|
|
111
|
+
editor.appendChild(textNode);
|
|
112
|
+
const inputEvent = new Event('input', { bubbles: true });
|
|
113
|
+
editor.dispatchEvent(inputEvent);
|
|
114
|
+
const changeEvent = new Event('change', { bubbles: true });
|
|
115
|
+
editor.dispatchEvent(changeEvent);
|
|
116
|
+
return true;
|
|
117
|
+
}, params.content);
|
|
118
|
+
if (!contentSet) {
|
|
119
|
+
throw new Error('无法找到内容编辑器');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
124
|
+
}
|
|
125
|
+
if (autoPublish) {
|
|
126
|
+
await waitAndClickPublish(page);
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
message: '已自动点击「发布」,请留意页面是否发布成功',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
success: true,
|
|
134
|
+
message: '已填入标题与正文;浏览器窗口保持打开,请在页面中确认后发布。',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
try {
|
|
139
|
+
if (browser.isConnected()) {
|
|
140
|
+
await browser.disconnect();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// 忽略断开时的异常
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=post.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post.js","sourceRoot":"","sources":["../../src/toolset/post.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAGtC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,mBAAmB,CAAC;AA2B3B,6DAA6D;AAC7D,KAAK,UAAU,mBAAmB,CAAC,IAAU;IAC3C,MAAM,SAAS,GAAG,CAAC,GAAG,wBAAwB,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,CAAC,GAAG,qBAAqB,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,CAAC,IAAc,EAAE,OAAiB,EAAE,EAAE;QACpD,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC3D,MAAM,EAAE,GAAG,CAAsB,CAAC;gBAClC,IAAI,EAAE,CAAC,QAAQ;oBAAE,SAAS;gBAC1B,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;oBAAE,OAAO,IAAI,CAAC;YAChE,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAE3E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CACjC,CAAC,IAAc,EAAE,OAAiB,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC3D,MAAM,EAAE,GAAG,CAAsB,CAAC;gBAClC,IAAI,EAAE,CAAC,QAAQ;oBAAE,SAAS;gBAC1B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;oBAAE,SAAS;gBAC5D,EAAE,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,EACD,SAAS,EACT,MAAM,CACP,CAAC;IAEF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAkB;IAC/C,mBAAmB,EAAE,CAAC;IACtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAmB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IAChE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC1C,MAAM,QAAQ,GACZ,IAAI,CAAC,QAAQ,KAAK,IAAI;QACtB,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG;QAChC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,MAAM,CAAC;IAErD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,CAAC,IAAI,CAAC,0CAA0C,EAAE;YAC1D,SAAS,EAAE,kBAAkB;YAC7B,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACpF,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,4EAA4E,EAAE;YAC5F,SAAS,EAAE,kBAAkB;YAC7B,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,iCAAiC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAClF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC;YACpE,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;YAChC,CAAC;YACD,MAAM,WAAW,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC;YAC5C,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;YAChD,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC1C,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,gDAAgD,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAChG,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAe,EAAE,EAAE;gBACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CACnC,gDAAgD,CAClC,CAAC;gBACjB,IAAI,CAAC,MAAM;oBAAE,OAAO,KAAK,CAAC;gBAC1B,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC;gBACtB,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;gBAClD,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC7B,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzD,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;gBACjC,MAAM,WAAW,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3D,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;gBAClC,OAAO,IAAI,CAAC;YACd,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACnB,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAChC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,uBAAuB;aACjC,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EACL,gCAAgC;SACnC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,IAAI,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC1B,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,WAAW;QACb,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type PostNoteParams = {
|
|
2
|
+
title?: string;
|
|
3
|
+
content: string;
|
|
4
|
+
tags?: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare function validatePostParams(params: PostNoteParams): void;
|
|
7
|
+
export declare function validateImagePaths(imagePaths: string[]): void;
|
|
8
|
+
//# sourceMappingURL=postValidate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postValidate.d.ts","sourceRoot":"","sources":["../../src/toolset/postValidate.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAqC/D;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAmB7D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
export function validatePostParams(params) {
|
|
3
|
+
if (!params.content || typeof params.content !== 'string') {
|
|
4
|
+
throw new Error('内容(content)是必需的且必须是字符串');
|
|
5
|
+
}
|
|
6
|
+
if (params.content.trim().length === 0) {
|
|
7
|
+
throw new Error('内容(content)不能为空');
|
|
8
|
+
}
|
|
9
|
+
if (params.content.length < 10) {
|
|
10
|
+
throw new Error('内容太短了,不能少于10个字');
|
|
11
|
+
}
|
|
12
|
+
if (params.content.length > 1000) {
|
|
13
|
+
throw new Error('小红书笔记长度不能超过1000个字');
|
|
14
|
+
}
|
|
15
|
+
if (params.title !== undefined) {
|
|
16
|
+
if (typeof params.title !== 'string') {
|
|
17
|
+
throw new Error('标题(title)必须是字符串');
|
|
18
|
+
}
|
|
19
|
+
if (params.title.length > 20) {
|
|
20
|
+
throw new Error('标题长度不能超过20个字符');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (params.tags !== undefined) {
|
|
24
|
+
if (!Array.isArray(params.tags)) {
|
|
25
|
+
throw new Error('标签(tags)必须是数组');
|
|
26
|
+
}
|
|
27
|
+
for (const tag of params.tags) {
|
|
28
|
+
if (typeof tag !== 'string') {
|
|
29
|
+
throw new Error('每个标签必须是字符串');
|
|
30
|
+
}
|
|
31
|
+
if (tag.length > 50) {
|
|
32
|
+
throw new Error('单个标签长度不能超过50个字符');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (params.tags.length > 10) {
|
|
36
|
+
throw new Error('标签数量不能超过10个');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function validateImagePaths(imagePaths) {
|
|
41
|
+
if (imagePaths.length === 0) {
|
|
42
|
+
throw new Error('至少需要一张图片');
|
|
43
|
+
}
|
|
44
|
+
if (imagePaths.length > 18) {
|
|
45
|
+
throw new Error(`图片数量不能超过18张,当前 ${imagePaths.length} 张`);
|
|
46
|
+
}
|
|
47
|
+
for (const imagePath of imagePaths) {
|
|
48
|
+
if (!existsSync(imagePath)) {
|
|
49
|
+
throw new Error(`图片文件不存在: ${imagePath}`);
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
readFileSync(imagePath);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`无法读取图片文件: ${imagePath} - ${error instanceof Error ? error.message : String(error)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=postValidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postValidate.js","sourceRoot":"","sources":["../../src/toolset/postValidate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** 发布页底部操作区(暂存离开 + 发布) */
|
|
2
|
+
export declare const PUBLISH_FOOTER_SELECTORS: readonly [".publish-page-publish-btn button.ce-btn.bg-red", ".publish-page-publish-btn button.bg-red"];
|
|
3
|
+
export declare const PUBLISH_BUTTON_LABELS: readonly ["发布", "立即发布"];
|
|
4
|
+
/** 在发布区容器内查找可点击的「发布」按钮(不扫描全页) */
|
|
5
|
+
export declare function findPublishButton(root: ParentNode, selectors?: readonly string[], labels?: readonly string[]): HTMLButtonElement | null;
|
|
6
|
+
export declare function isPublishButtonReady(root: ParentNode, selectors?: readonly string[], labels?: readonly string[]): boolean;
|
|
7
|
+
//# sourceMappingURL=publishButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publishButton.d.ts","sourceRoot":"","sources":["../../src/toolset/publishButton.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,eAAO,MAAM,wBAAwB,wGAG3B,CAAC;AAEX,eAAO,MAAM,qBAAqB,yBAA0B,CAAC;AAM7D,iCAAiC;AACjC,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,SAAS,GAAE,SAAS,MAAM,EAA6B,EACvD,MAAM,GAAE,SAAS,MAAM,EAA0B,GAChD,iBAAiB,GAAG,IAAI,CAY1B;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,SAAS,GAAE,SAAS,MAAM,EAA6B,EACvD,MAAM,GAAE,SAAS,MAAM,EAA0B,GAChD,OAAO,CAET"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** 发布页底部操作区(暂存离开 + 发布) */
|
|
2
|
+
export const PUBLISH_FOOTER_SELECTORS = [
|
|
3
|
+
'.publish-page-publish-btn button.ce-btn.bg-red',
|
|
4
|
+
'.publish-page-publish-btn button.bg-red',
|
|
5
|
+
];
|
|
6
|
+
export const PUBLISH_BUTTON_LABELS = ['发布', '立即发布'];
|
|
7
|
+
function normalizeLabel(text) {
|
|
8
|
+
return text.replace(/\s+/g, '');
|
|
9
|
+
}
|
|
10
|
+
/** 在发布区容器内查找可点击的「发布」按钮(不扫描全页) */
|
|
11
|
+
export function findPublishButton(root, selectors = PUBLISH_FOOTER_SELECTORS, labels = PUBLISH_BUTTON_LABELS) {
|
|
12
|
+
const allowed = new Set(labels.map(normalizeLabel));
|
|
13
|
+
for (const sel of selectors) {
|
|
14
|
+
for (const b of Array.from(root.querySelectorAll(sel))) {
|
|
15
|
+
const el = b;
|
|
16
|
+
if (el.disabled)
|
|
17
|
+
continue;
|
|
18
|
+
if (allowed.has(normalizeLabel(el.textContent ?? ''))) {
|
|
19
|
+
return el;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function isPublishButtonReady(root, selectors = PUBLISH_FOOTER_SELECTORS, labels = PUBLISH_BUTTON_LABELS) {
|
|
26
|
+
return findPublishButton(root, selectors, labels) !== null;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=publishButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publishButton.js","sourceRoot":"","sources":["../../src/toolset/publishButton.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,gDAAgD;IAChD,yCAAyC;CACjC,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAU,CAAC;AAE7D,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,iBAAiB,CAC/B,IAAgB,EAChB,YAA+B,wBAAwB,EACvD,SAA4B,qBAAqB;IAEjD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACpD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACvD,MAAM,EAAE,GAAG,CAAsB,CAAC;YAClC,IAAI,EAAE,CAAC,QAAQ;gBAAE,SAAS;YAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;gBACtD,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,IAAgB,EAChB,YAA+B,wBAAwB,EACvD,SAA4B,qBAAqB;IAEjD,OAAO,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type PublishedRecord = {
|
|
2
|
+
id: string;
|
|
3
|
+
account: string;
|
|
4
|
+
sourceDraftId?: string | null;
|
|
5
|
+
title: string;
|
|
6
|
+
content: string;
|
|
7
|
+
images: string[];
|
|
8
|
+
publishedAt: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function appendPublishedRecord(row: Omit<PublishedRecord, 'id'> & {
|
|
11
|
+
id?: string;
|
|
12
|
+
}): PublishedRecord;
|
|
13
|
+
export declare function listPublished(accountFilter?: string): PublishedRecord[];
|
|
14
|
+
export declare function formatPublishedList(items: PublishedRecord[]): string;
|
|
15
|
+
//# sourceMappingURL=publishedRecords.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publishedRecords.d.ts","sourceRoot":"","sources":["../../src/toolset/publishedRecords.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAYF,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,eAAe,CAgBjB;AAED,wBAAgB,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAoBvE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CAUpE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, renameSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { randomUUID } from 'crypto';
|
|
4
|
+
import { PUBLISHED_ROOT, ensureAppDataLayout } from '../config.js';
|
|
5
|
+
function recordPath(id) {
|
|
6
|
+
return join(PUBLISHED_ROOT, `${id}.json`);
|
|
7
|
+
}
|
|
8
|
+
function atomicWrite(path, data) {
|
|
9
|
+
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
10
|
+
writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf-8');
|
|
11
|
+
renameSync(tmp, path);
|
|
12
|
+
}
|
|
13
|
+
export function appendPublishedRecord(row) {
|
|
14
|
+
ensureAppDataLayout();
|
|
15
|
+
if (!existsSync(PUBLISHED_ROOT)) {
|
|
16
|
+
mkdirSync(PUBLISHED_ROOT, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
const rec = {
|
|
19
|
+
id: row.id ?? randomUUID(),
|
|
20
|
+
account: row.account,
|
|
21
|
+
sourceDraftId: row.sourceDraftId ?? null,
|
|
22
|
+
title: row.title,
|
|
23
|
+
content: row.content,
|
|
24
|
+
images: [...row.images],
|
|
25
|
+
publishedAt: row.publishedAt,
|
|
26
|
+
};
|
|
27
|
+
atomicWrite(recordPath(rec.id), rec);
|
|
28
|
+
return rec;
|
|
29
|
+
}
|
|
30
|
+
export function listPublished(accountFilter) {
|
|
31
|
+
ensureAppDataLayout();
|
|
32
|
+
if (!existsSync(PUBLISHED_ROOT)) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const out = [];
|
|
36
|
+
for (const name of readdirSync(PUBLISHED_ROOT)) {
|
|
37
|
+
if (!name.endsWith('.json'))
|
|
38
|
+
continue;
|
|
39
|
+
try {
|
|
40
|
+
const rec = JSON.parse(readFileSync(join(PUBLISHED_ROOT, name), 'utf-8'));
|
|
41
|
+
if (accountFilter && rec.account !== accountFilter)
|
|
42
|
+
continue;
|
|
43
|
+
out.push(rec);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
out.sort((a, b) => b.publishedAt.localeCompare(a.publishedAt));
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
export function formatPublishedList(items) {
|
|
53
|
+
if (items.length === 0) {
|
|
54
|
+
return '暂无已发布的本地归档记录。';
|
|
55
|
+
}
|
|
56
|
+
return items
|
|
57
|
+
.map((r) => `${r.id}\t${r.account}\t${r.publishedAt}\t${r.title.replace(/\s+/g, ' ').slice(0, 36)}`)
|
|
58
|
+
.join('\n');
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=publishedRecords.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publishedRecords.js","sourceRoot":"","sources":["../../src/toolset/publishedRecords.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ResolvedSession } from './sessionTypes.js';
|
|
2
|
+
/**
|
|
3
|
+
* 解析本次命令使用的账号 slug。
|
|
4
|
+
*
|
|
5
|
+
* 优先级:`--account` 显式指定 → registry `currentAccount`(须已通过 `xhs account use` 设置)。
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveAccountSlug(explicitAccount?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* 解析本次命令使用的 Puppeteer userDataDir 与缓存前缀。
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveSession(explicitAccount?: string): ResolvedSession;
|
|
12
|
+
//# sourceMappingURL=sessionResolve.d.ts.map
|