@skills-store/rednote 0.1.2 → 0.1.5
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/dist/rednote/getMyProfile.js +0 -0
- package/dist/rednote/index.js +2 -5
- package/dist/rednote/login.js +16 -19
- package/dist/rednote/publish.js +216 -95
- package/package.json +10 -1
- package/README.md +0 -35
- package/bin/rednote.js +0 -19
|
File without changes
|
package/dist/rednote/index.js
CHANGED
|
@@ -95,11 +95,8 @@ export async function runRednoteCli(argv = process.argv.slice(2)) {
|
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
if (command === 'publish') {
|
|
98
|
-
const { runPublishCommand } = await import('./publish.js');
|
|
99
|
-
await runPublishCommand(
|
|
100
|
-
instance: basicValues.instance,
|
|
101
|
-
help: basicValues.help
|
|
102
|
-
});
|
|
98
|
+
const { parsePublishCliArgs, runPublishCommand } = await import('./publish.js');
|
|
99
|
+
await runPublishCommand(parsePublishCliArgs(commandArgv));
|
|
103
100
|
return;
|
|
104
101
|
}
|
|
105
102
|
if (command === 'home') {
|
package/dist/rednote/login.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { parseArgs } from 'node:util';
|
|
3
3
|
import { printJson, runCli } from '../utils/browser-cli.js';
|
|
4
4
|
import { resolveStatusTarget } from './status.js';
|
|
5
|
-
import { createRednoteSession, disconnectRednoteSession } from './checkLogin.js';
|
|
5
|
+
import { checkRednoteLogin, createRednoteSession, disconnectRednoteSession } from './checkLogin.js';
|
|
6
6
|
function printLoginHelp() {
|
|
7
7
|
process.stdout.write(`rednote login
|
|
8
8
|
|
|
@@ -28,21 +28,24 @@ async function getOrCreateXiaohongshuPage(session) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
export async function openRednoteLogin(target, session) {
|
|
31
|
+
const rednoteStatus = await checkRednoteLogin(target, session);
|
|
32
|
+
if (!rednoteStatus.needLogin) {
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
rednote: {
|
|
36
|
+
loginClicked: false,
|
|
37
|
+
pageUrl: session.page.url(),
|
|
38
|
+
waitingForPhoneLogin: false,
|
|
39
|
+
message: '当前实例已登录,无需重复执行登录操作。'
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
31
43
|
const { page } = await getOrCreateXiaohongshuPage(session);
|
|
32
|
-
await page.waitForTimeout(2_000);
|
|
33
44
|
const loginButton = page.locator('#login-btn');
|
|
34
45
|
const hasLoginButton = await loginButton.count() > 0;
|
|
35
46
|
if (!hasLoginButton) {
|
|
36
47
|
return {
|
|
37
48
|
ok: true,
|
|
38
|
-
instance: {
|
|
39
|
-
scope: target.scope,
|
|
40
|
-
name: target.instanceName,
|
|
41
|
-
browser: target.browser,
|
|
42
|
-
userDataDir: target.userDataDir,
|
|
43
|
-
source: target.source,
|
|
44
|
-
lastConnect: target.lastConnect
|
|
45
|
-
},
|
|
46
49
|
rednote: {
|
|
47
50
|
loginClicked: false,
|
|
48
51
|
pageUrl: page.url(),
|
|
@@ -51,18 +54,12 @@ export async function openRednoteLogin(target, session) {
|
|
|
51
54
|
}
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
|
-
await loginButton.first().click(
|
|
57
|
+
await loginButton.first().click({
|
|
58
|
+
timeout: 2000
|
|
59
|
+
});
|
|
55
60
|
await page.waitForTimeout(500);
|
|
56
61
|
return {
|
|
57
62
|
ok: true,
|
|
58
|
-
instance: {
|
|
59
|
-
scope: target.scope,
|
|
60
|
-
name: target.instanceName,
|
|
61
|
-
browser: target.browser,
|
|
62
|
-
userDataDir: target.userDataDir,
|
|
63
|
-
source: target.source,
|
|
64
|
-
lastConnect: target.lastConnect
|
|
65
|
-
},
|
|
66
63
|
rednote: {
|
|
67
64
|
loginClicked: true,
|
|
68
65
|
pageUrl: page.url(),
|
package/dist/rednote/publish.js
CHANGED
|
@@ -8,6 +8,27 @@ import { createRednoteSession, disconnectRednoteSession, ensureRednoteLoggedIn }
|
|
|
8
8
|
const REDNOTE_EXPLORE_URL = 'https://www.xiaohongshu.com/explore';
|
|
9
9
|
const CREATOR_HOME_URL = 'https://creator.xiaohongshu.com/new/home';
|
|
10
10
|
const CREATOR_SERVICE_SELECTOR = 'a.link[href="//creator.xiaohongshu.com/?source=official"]';
|
|
11
|
+
const CREATOR_CENTER_TRIGGER_SELECTOR = 'span.reds-button-new-text';
|
|
12
|
+
const CREATOR_CENTER_TRIGGER_TEXT = '创作中心';
|
|
13
|
+
const PUBLISH_NOTE_BUTTON_SELECTOR = 'span.btn-text';
|
|
14
|
+
const PUBLISH_NOTE_BUTTON_TEXT = '发布笔记';
|
|
15
|
+
const PUBLISH_TYPE_TAB_SELECTOR = '.header-tabs .creator-tab .title';
|
|
16
|
+
const VIDEO_UPLOAD_BUTTON_SELECTOR = 'button.upload-button';
|
|
17
|
+
const VIDEO_UPLOAD_BUTTON_TEXT = '上传视频';
|
|
18
|
+
const IMAGE_UPLOAD_BUTTON_SELECTOR = 'button.upload-button';
|
|
19
|
+
const IMAGE_UPLOAD_BUTTON_TEXT = '上传图片';
|
|
20
|
+
const IMAGE_PUBLISH_PAGE_SELECTOR = '.publish-page-content';
|
|
21
|
+
const IMAGE_TITLE_INPUT_SELECTOR = '.publish-page-content input.d-text[placeholder="填写标题会有更多赞哦"]';
|
|
22
|
+
const IMAGE_CONTENT_EDITOR_SELECTOR = '.editor-content .tiptap.ProseMirror[contenteditable="true"]';
|
|
23
|
+
const ARTICLE_NEW_BUTTON_SELECTOR = 'button.new-btn';
|
|
24
|
+
const ARTICLE_NEW_BUTTON_TEXT = '新的创作';
|
|
25
|
+
const ARTICLE_TITLE_INPUT_SELECTOR = '.edit-page textarea.d-text[placeholder="输入标题"]';
|
|
26
|
+
const ARTICLE_CONTENT_EDITOR_SELECTOR = '.rich-editor-content .tiptap.ProseMirror[contenteditable="true"]';
|
|
27
|
+
const PUBLISH_ACTION_BUTTON_SELECTOR = '.publish-page-publish-btn button';
|
|
28
|
+
const SAVE_DRAFT_BUTTON_TEXT = '暂存离开';
|
|
29
|
+
const PUBLISH_BUTTON_TEXT = '发布';
|
|
30
|
+
const VIDEO_FILE_INPUT_SELECTOR = 'input[type="file"]';
|
|
31
|
+
const IMAGE_FILE_INPUT_SELECTOR = 'input[type="file"]';
|
|
11
32
|
const MAX_IMAGE_COUNT = 15;
|
|
12
33
|
function printPublishHelp() {
|
|
13
34
|
process.stdout.write(`rednote publish
|
|
@@ -191,62 +212,6 @@ export function resolvePublishPayload(values) {
|
|
|
191
212
|
content
|
|
192
213
|
};
|
|
193
214
|
}
|
|
194
|
-
function summarizePayload(payload) {
|
|
195
|
-
if (payload.type === 'video') {
|
|
196
|
-
return {
|
|
197
|
-
type: payload.type,
|
|
198
|
-
title: payload.title,
|
|
199
|
-
content: payload.content,
|
|
200
|
-
tags: payload.tags,
|
|
201
|
-
draft: payload.draft,
|
|
202
|
-
assetCount: 1,
|
|
203
|
-
coverImagePath: null,
|
|
204
|
-
videoPath: payload.videoPath,
|
|
205
|
-
imagePaths: [],
|
|
206
|
-
contentLength: payload.content.length
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
if (payload.type === 'image') {
|
|
210
|
-
return {
|
|
211
|
-
type: payload.type,
|
|
212
|
-
title: payload.title,
|
|
213
|
-
content: payload.content,
|
|
214
|
-
tags: payload.tags,
|
|
215
|
-
draft: payload.draft,
|
|
216
|
-
assetCount: payload.imagePaths.length,
|
|
217
|
-
coverImagePath: payload.coverImagePath,
|
|
218
|
-
videoPath: null,
|
|
219
|
-
imagePaths: payload.imagePaths,
|
|
220
|
-
contentLength: payload.content.length
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
type: payload.type,
|
|
225
|
-
title: payload.title,
|
|
226
|
-
content: payload.content,
|
|
227
|
-
tags: [],
|
|
228
|
-
draft: payload.draft,
|
|
229
|
-
assetCount: 0,
|
|
230
|
-
coverImagePath: null,
|
|
231
|
-
videoPath: null,
|
|
232
|
-
imagePaths: [],
|
|
233
|
-
contentLength: payload.content.length
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
function toPublishResult(target, publish) {
|
|
237
|
-
return {
|
|
238
|
-
ok: true,
|
|
239
|
-
instance: {
|
|
240
|
-
scope: target.scope,
|
|
241
|
-
name: target.instanceName,
|
|
242
|
-
browser: target.browser,
|
|
243
|
-
userDataDir: target.userDataDir,
|
|
244
|
-
source: target.source,
|
|
245
|
-
lastConnect: target.lastConnect
|
|
246
|
-
},
|
|
247
|
-
publish
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
215
|
function getSessionPages(session) {
|
|
251
216
|
const pages = [
|
|
252
217
|
session.page,
|
|
@@ -303,51 +268,207 @@ async function waitForCreatorHome(page) {
|
|
|
303
268
|
});
|
|
304
269
|
await page.waitForLoadState('domcontentloaded');
|
|
305
270
|
}
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
message: '当前页面已经是创作服务首页,发布参数已校验。'
|
|
318
|
-
});
|
|
271
|
+
async function findVisibleLocator(locator, timeoutMs = 3_000) {
|
|
272
|
+
const deadline = Date.now() + timeoutMs;
|
|
273
|
+
while(Date.now() < deadline){
|
|
274
|
+
const count = await locator.count();
|
|
275
|
+
for(let index = 0; index < count; index += 1){
|
|
276
|
+
const candidate = locator.nth(index);
|
|
277
|
+
if (await candidate.isVisible().catch(()=>false)) {
|
|
278
|
+
return candidate;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
await new Promise((resolve)=>setTimeout(resolve, 100));
|
|
319
282
|
}
|
|
320
|
-
|
|
321
|
-
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
async function requireVisibleLocator(locator, errorMessage, timeoutMs = 3_000) {
|
|
286
|
+
const visibleLocator = await findVisibleLocator(locator, timeoutMs);
|
|
287
|
+
if (!visibleLocator) {
|
|
288
|
+
throw new Error(errorMessage);
|
|
289
|
+
}
|
|
290
|
+
return visibleLocator;
|
|
291
|
+
}
|
|
292
|
+
function resolvePublishTypeTabText(type) {
|
|
293
|
+
if (type === 'video') {
|
|
294
|
+
return '上传视频';
|
|
295
|
+
}
|
|
296
|
+
if (type === 'image') {
|
|
297
|
+
return '上传图文';
|
|
298
|
+
}
|
|
299
|
+
return '写长文';
|
|
300
|
+
}
|
|
301
|
+
async function hoverCreatorCenter(page) {
|
|
302
|
+
const creatorCenterTrigger = page.locator(CREATOR_CENTER_TRIGGER_SELECTOR).filter({
|
|
303
|
+
hasText: CREATOR_CENTER_TRIGGER_TEXT
|
|
322
304
|
});
|
|
323
|
-
|
|
324
|
-
|
|
305
|
+
const visibleCreatorCenterTrigger = await findVisibleLocator(creatorCenterTrigger);
|
|
306
|
+
if (!visibleCreatorCenterTrigger) {
|
|
307
|
+
return;
|
|
325
308
|
}
|
|
326
|
-
|
|
309
|
+
await visibleCreatorCenterTrigger.hover();
|
|
310
|
+
await page.waitForTimeout(300);
|
|
311
|
+
}
|
|
312
|
+
async function openPublishComposer(page, type) {
|
|
313
|
+
const publishNoteButton = page.locator(PUBLISH_NOTE_BUTTON_SELECTOR).filter({
|
|
314
|
+
hasText: PUBLISH_NOTE_BUTTON_TEXT
|
|
315
|
+
});
|
|
316
|
+
const visiblePublishNoteButton = await requireVisibleLocator(publishNoteButton, '未找到“发布笔记”按钮,请确认已进入创作服务首页。', 15_000);
|
|
317
|
+
await visiblePublishNoteButton.click();
|
|
318
|
+
const publishTypeTabText = resolvePublishTypeTabText(type);
|
|
319
|
+
const publishTypeTab = page.locator(PUBLISH_TYPE_TAB_SELECTOR).filter({
|
|
320
|
+
hasText: publishTypeTabText
|
|
321
|
+
}).last();
|
|
322
|
+
const visiblePublishTypeTab = await requireVisibleLocator(publishTypeTab, `未找到“${publishTypeTabText}”入口,请确认创作服务页面已正确加载。`, 15_000);
|
|
323
|
+
await visiblePublishTypeTab.click();
|
|
324
|
+
await page.waitForTimeout(300);
|
|
325
|
+
}
|
|
326
|
+
async function uploadVideoFile(page, videoPath) {
|
|
327
|
+
const uploadVideoButton = page.locator(VIDEO_UPLOAD_BUTTON_SELECTOR).filter({
|
|
328
|
+
hasText: VIDEO_UPLOAD_BUTTON_TEXT
|
|
329
|
+
});
|
|
330
|
+
const visibleUploadVideoButton = await requireVisibleLocator(uploadVideoButton, '未找到“上传视频”按钮,请确认已进入视频发布页。', 15_000);
|
|
331
|
+
const fileChooserPromise = page.waitForEvent('filechooser', {
|
|
327
332
|
timeout: 3_000
|
|
328
333
|
}).catch(()=>null);
|
|
329
|
-
await
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
334
|
+
await visibleUploadVideoButton.click();
|
|
335
|
+
const fileChooser = await fileChooserPromise;
|
|
336
|
+
if (fileChooser) {
|
|
337
|
+
await fileChooser.setFiles(videoPath);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const videoFileInput = page.locator(`${VIDEO_FILE_INPUT_SELECTOR}[accept*="video"], ${VIDEO_FILE_INPUT_SELECTOR}`);
|
|
341
|
+
if (await videoFileInput.count() === 0) {
|
|
342
|
+
throw new Error('未找到视频文件输入框,请确认上传组件已正确加载。');
|
|
343
|
+
}
|
|
344
|
+
await videoFileInput.first().setInputFiles(videoPath);
|
|
345
|
+
}
|
|
346
|
+
async function uploadImageFiles(page, imagePaths) {
|
|
347
|
+
const uploadImageButton = page.locator(IMAGE_UPLOAD_BUTTON_SELECTOR).filter({
|
|
348
|
+
hasText: IMAGE_UPLOAD_BUTTON_TEXT
|
|
349
|
+
});
|
|
350
|
+
const visibleUploadImageButton = await requireVisibleLocator(uploadImageButton, '未找到“上传图片”按钮,请确认已进入图文发布页。', 15_000);
|
|
351
|
+
const fileChooserPromise = page.waitForEvent('filechooser', {
|
|
352
|
+
timeout: 3_000
|
|
353
|
+
}).catch(()=>null);
|
|
354
|
+
await visibleUploadImageButton.click();
|
|
355
|
+
const fileChooser = await fileChooserPromise;
|
|
356
|
+
if (fileChooser) {
|
|
357
|
+
await fileChooser.setFiles(imagePaths);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const imageFileInput = page.locator(`${IMAGE_FILE_INPUT_SELECTOR}[accept*="image"], ${IMAGE_FILE_INPUT_SELECTOR}`);
|
|
361
|
+
if (await imageFileInput.count() === 0) {
|
|
362
|
+
throw new Error('未找到图片文件输入框,请确认上传组件已正确加载。');
|
|
363
|
+
}
|
|
364
|
+
await imageFileInput.first().setInputFiles(imagePaths);
|
|
365
|
+
}
|
|
366
|
+
async function waitForImagePublishPage(page) {
|
|
367
|
+
const imagePublishPage = page.locator(IMAGE_PUBLISH_PAGE_SELECTOR);
|
|
368
|
+
const visibleImagePublishPage = await requireVisibleLocator(imagePublishPage, '未等待到图文发布页,请确认图片上传成功。', 30_000);
|
|
369
|
+
await visibleImagePublishPage.waitFor({
|
|
370
|
+
state: 'visible',
|
|
371
|
+
timeout: 30_000
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
async function fillImageTitle(page, title) {
|
|
375
|
+
const imageTitleInput = page.locator(IMAGE_TITLE_INPUT_SELECTOR);
|
|
376
|
+
const visibleImageTitleInput = await requireVisibleLocator(imageTitleInput, '未找到图文标题输入框,请确认图文发布页已正确加载。', 15_000);
|
|
377
|
+
await visibleImageTitleInput.fill(title);
|
|
378
|
+
await page.waitForTimeout(200);
|
|
379
|
+
}
|
|
380
|
+
async function fillImageContent(page, content) {
|
|
381
|
+
const imageContentEditor = page.locator(IMAGE_CONTENT_EDITOR_SELECTOR);
|
|
382
|
+
const visibleImageContentEditor = await requireVisibleLocator(imageContentEditor, '未找到图文正文编辑器,请确认图文发布页已正确加载。', 15_000);
|
|
383
|
+
await visibleImageContentEditor.fill(content);
|
|
384
|
+
await page.waitForTimeout(200);
|
|
385
|
+
}
|
|
386
|
+
async function openArticleEditor(page) {
|
|
387
|
+
const articleNewButton = page.locator(ARTICLE_NEW_BUTTON_SELECTOR).filter({
|
|
388
|
+
hasText: ARTICLE_NEW_BUTTON_TEXT
|
|
389
|
+
});
|
|
390
|
+
const visibleArticleNewButton = await requireVisibleLocator(articleNewButton, '未找到“新的创作”按钮,请确认已进入长文发布页。', 15_000);
|
|
391
|
+
await visibleArticleNewButton.click();
|
|
392
|
+
await page.waitForTimeout(300);
|
|
393
|
+
}
|
|
394
|
+
async function fillArticleTitle(page, title) {
|
|
395
|
+
const articleTitleInput = page.locator(ARTICLE_TITLE_INPUT_SELECTOR);
|
|
396
|
+
const visibleArticleTitleInput = await requireVisibleLocator(articleTitleInput, '未找到长文标题输入框,请确认已进入长文编辑页。', 15_000);
|
|
397
|
+
await visibleArticleTitleInput.fill(title);
|
|
398
|
+
await page.waitForTimeout(200);
|
|
399
|
+
}
|
|
400
|
+
async function fillArticleContent(page, content) {
|
|
401
|
+
const articleContentEditor = page.locator(ARTICLE_CONTENT_EDITOR_SELECTOR);
|
|
402
|
+
const visibleArticleContentEditor = await requireVisibleLocator(articleContentEditor, '未找到长文正文编辑器,请确认已进入长文编辑页。', 15_000);
|
|
403
|
+
await visibleArticleContentEditor.fill(content);
|
|
404
|
+
await page.waitForTimeout(200);
|
|
405
|
+
}
|
|
406
|
+
async function preparePublishAssets(page, payload) {
|
|
407
|
+
if (payload.type === 'video') {
|
|
408
|
+
await uploadVideoFile(page, payload.videoPath);
|
|
409
|
+
await waitForImagePublishPage(page);
|
|
410
|
+
await fillImageTitle(page, payload.title);
|
|
411
|
+
await fillImageContent(page, payload.content);
|
|
412
|
+
return;
|
|
341
413
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
414
|
+
if (payload.type === 'image') {
|
|
415
|
+
await uploadImageFiles(page, payload.imagePaths);
|
|
416
|
+
await waitForImagePublishPage(page);
|
|
417
|
+
await fillImageTitle(page, payload.title);
|
|
418
|
+
await fillImageContent(page, payload.content);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
await openArticleEditor(page);
|
|
422
|
+
await fillArticleTitle(page, payload.title);
|
|
423
|
+
await fillArticleContent(page, payload.content);
|
|
424
|
+
}
|
|
425
|
+
async function finalizePublish(page, draft) {
|
|
426
|
+
const buttonText = draft ? SAVE_DRAFT_BUTTON_TEXT : PUBLISH_BUTTON_TEXT;
|
|
427
|
+
const publishActionButton = page.locator(PUBLISH_ACTION_BUTTON_SELECTOR).filter({
|
|
428
|
+
hasText: buttonText
|
|
350
429
|
});
|
|
430
|
+
const visiblePublishActionButton = await requireVisibleLocator(publishActionButton, `未找到“${buttonText}”按钮,请确认发布页已正确加载。`, 15_000);
|
|
431
|
+
await visiblePublishActionButton.click();
|
|
432
|
+
await page.waitForTimeout(500);
|
|
433
|
+
}
|
|
434
|
+
export async function openRednotePublish(session, payload) {
|
|
435
|
+
const resolved = await resolvePublishPage(session);
|
|
436
|
+
let targetPage = resolved.page;
|
|
437
|
+
let clickedCreatorService = false;
|
|
438
|
+
let reusedCreatorHome = resolved.reusedCreatorHome || isCreatorHomeUrl(resolved.page.url());
|
|
439
|
+
let openedInNewPage = false;
|
|
440
|
+
if (!reusedCreatorHome) {
|
|
441
|
+
await hoverCreatorCenter(resolved.page);
|
|
442
|
+
const creatorServiceLink = resolved.page.locator(CREATOR_SERVICE_SELECTOR).filter({
|
|
443
|
+
hasText: '创作服务'
|
|
444
|
+
});
|
|
445
|
+
const visibleCreatorServiceLink = await requireVisibleLocator(creatorServiceLink, '未找到“创作服务”入口,请先打开小红书首页并确认账号已登录。');
|
|
446
|
+
const popupPromise = session.browserContext.waitForEvent('page', {
|
|
447
|
+
timeout: 3_000
|
|
448
|
+
}).catch(()=>null);
|
|
449
|
+
await visibleCreatorServiceLink.click();
|
|
450
|
+
targetPage = await popupPromise ?? resolved.page;
|
|
451
|
+
openedInNewPage = targetPage !== resolved.page;
|
|
452
|
+
try {
|
|
453
|
+
await waitForCreatorHome(targetPage);
|
|
454
|
+
} catch {
|
|
455
|
+
const existingCreatorHomePage = getSessionPages(session).find((page)=>isCreatorHomeUrl(page.url()));
|
|
456
|
+
if (!existingCreatorHomePage) {
|
|
457
|
+
throw new Error(`点击“创作服务”后,未跳转到 ${CREATOR_HOME_URL}`);
|
|
458
|
+
}
|
|
459
|
+
targetPage = existingCreatorHomePage;
|
|
460
|
+
openedInNewPage = targetPage !== resolved.page;
|
|
461
|
+
}
|
|
462
|
+
clickedCreatorService = true;
|
|
463
|
+
reusedCreatorHome = false;
|
|
464
|
+
}
|
|
465
|
+
await openPublishComposer(targetPage, payload.type);
|
|
466
|
+
await preparePublishAssets(targetPage, payload);
|
|
467
|
+
await finalizePublish(targetPage, payload.draft);
|
|
468
|
+
return {
|
|
469
|
+
ok: true,
|
|
470
|
+
message: payload.draft ? '已完成发布页操作并点击“暂存离开”,内容已保存到草稿。' : '已完成发布页操作并点击“发布”。'
|
|
471
|
+
};
|
|
351
472
|
}
|
|
352
473
|
export async function runPublishCommand(values) {
|
|
353
474
|
if (values.help || !hasPublishInputs(values)) {
|
|
@@ -359,7 +480,7 @@ export async function runPublishCommand(values) {
|
|
|
359
480
|
const session = await createRednoteSession(target);
|
|
360
481
|
try {
|
|
361
482
|
await ensureRednoteLoggedIn(target, 'publishing content', session);
|
|
362
|
-
const result = await openRednotePublish(
|
|
483
|
+
const result = await openRednotePublish(session, payload);
|
|
363
484
|
printJson(result);
|
|
364
485
|
} finally{
|
|
365
486
|
disconnectRednoteSession(session);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skills-store/rednote",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,6 +45,15 @@
|
|
|
45
45
|
"bun": ">=1.0.0"
|
|
46
46
|
},
|
|
47
47
|
"description": "Xiaohongshu automation CLI for Skills Router",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/skills-router/skills-store.git",
|
|
51
|
+
"directory": "packages/rednote"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/skills-router/skills-store/tree/main/packages/rednote",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/skills-router/skills-store/issues"
|
|
56
|
+
},
|
|
48
57
|
"publishConfig": {
|
|
49
58
|
"access": "public"
|
|
50
59
|
},
|
package/README.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# rednote
|
|
2
|
-
|
|
3
|
-
A Xiaohongshu (RED) automation CLI.
|
|
4
|
-
|
|
5
|
-
## Run with npx
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npx -y @skills-store/rednote browser list
|
|
9
|
-
npx -y @skills-store/rednote browser create --name seller-main --browser chrome
|
|
10
|
-
npx -y @skills-store/rednote browser connect --instance seller-main
|
|
11
|
-
npx -y @skills-store/rednote login --instance seller-main
|
|
12
|
-
npx -y @skills-store/rednote search --instance seller-main --keyword 护肤
|
|
13
|
-
npx -y @skills-store/rednote get-feed-detail --instance seller-main --url "https://www.xiaohongshu.com/explore/<id>?xsec_token=<token>"
|
|
14
|
-
npx -y @skills-store/rednote get-profile --instance seller-main --id USER_ID
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Install globally
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install -g @skills-store/rednote
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Storage
|
|
24
|
-
|
|
25
|
-
The CLI stores custom browser instances and metadata under:
|
|
26
|
-
|
|
27
|
-
```text
|
|
28
|
-
~/.skills-router/rednote/instances
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Run this to inspect the current environment and exact resolved paths:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npx -y @skills-store/rednote env
|
|
35
|
-
```
|
package/bin/rednote.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { runRootCli } from '../dist/index.js';
|
|
3
|
-
import { stringifyError } from '../dist/utils/browser-cli.js';
|
|
4
|
-
|
|
5
|
-
try {
|
|
6
|
-
await runRootCli(process.argv.slice(2));
|
|
7
|
-
} catch (error) {
|
|
8
|
-
process.stderr.write(
|
|
9
|
-
`${JSON.stringify(
|
|
10
|
-
{
|
|
11
|
-
ok: false,
|
|
12
|
-
error: stringifyError(error),
|
|
13
|
-
},
|
|
14
|
-
null,
|
|
15
|
-
2,
|
|
16
|
-
)}\n`,
|
|
17
|
-
);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|