@skrillex1224/chrome-article-publish-extension 1.0.0

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.
@@ -0,0 +1,1900 @@
1
+ import {
2
+ BaijiahaoAdapter,
3
+ BilibiliAdapter,
4
+ CSDNAdapter,
5
+ CnblogsAdapter,
6
+ CodeAdapter,
7
+ Cto51Adapter,
8
+ DouyinAdapter,
9
+ JuejinAdapter,
10
+ NeteaseAdapter,
11
+ SohuAdapter,
12
+ TencentAdapter,
13
+ ToutiaoAdapter,
14
+ WeixinAdapter
15
+ } from "../chunk-QM2T4VGM.js";
16
+ import {
17
+ markdownToDraft
18
+ } from "../chunk-IUAZAMKF.js";
19
+ import {
20
+ createLogger,
21
+ htmlToMarkdown,
22
+ parseMarkdownImages
23
+ } from "../chunk-7Q3KKWUB.js";
24
+
25
+ // src/adapters/types.ts
26
+ var DEFAULT_PREPROCESS_CONFIG = {
27
+ outputFormat: "html",
28
+ removeIframes: true,
29
+ removeComments: true,
30
+ removeSpecialTags: true,
31
+ removeSvgImages: true,
32
+ processCodeBlocks: true,
33
+ processLazyImages: true,
34
+ removeEmptyElements: true,
35
+ removeDataAttributes: true,
36
+ removeSrcset: true,
37
+ removeSizes: true
38
+ };
39
+
40
+ // src/adapters/base.ts
41
+ var BaseAdapter = class {
42
+ runtime;
43
+ context = {};
44
+ async init(runtime) {
45
+ this.runtime = runtime;
46
+ }
47
+ /**
48
+ * 发送请求
49
+ */
50
+ async request(url, options = {}) {
51
+ const response = await this.runtime.fetch(url, {
52
+ ...options,
53
+ headers: {
54
+ "Content-Type": "application/json",
55
+ ...options.headers
56
+ }
57
+ });
58
+ if (!response.ok) {
59
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
60
+ }
61
+ const contentType = response.headers.get("content-type");
62
+ if (contentType?.includes("application/json")) {
63
+ return response.json();
64
+ }
65
+ return response.text();
66
+ }
67
+ /**
68
+ * 带重试的请求
69
+ */
70
+ async requestWithRetry(url, options = {}, maxRetries = 3) {
71
+ let lastError = null;
72
+ for (let i = 0; i < maxRetries; i++) {
73
+ try {
74
+ return await this.request(url, options);
75
+ } catch (error) {
76
+ lastError = error;
77
+ if (i < maxRetries - 1) {
78
+ await this.delay(1e3 * (i + 1));
79
+ }
80
+ }
81
+ }
82
+ throw lastError;
83
+ }
84
+ /**
85
+ * 延迟
86
+ */
87
+ delay(ms) {
88
+ return new Promise((resolve) => setTimeout(resolve, ms));
89
+ }
90
+ /**
91
+ * 创建同步结果
92
+ */
93
+ createResult(success, data) {
94
+ return {
95
+ platform: this.meta.id,
96
+ success,
97
+ timestamp: Date.now(),
98
+ ...data
99
+ };
100
+ }
101
+ };
102
+
103
+ // src/adapters/registry.ts
104
+ var AdapterRegistry = class {
105
+ adapters = /* @__PURE__ */ new Map();
106
+ instances = /* @__PURE__ */ new Map();
107
+ runtime;
108
+ /**
109
+ * 设置运行时
110
+ */
111
+ setRuntime(runtime) {
112
+ this.runtime = runtime;
113
+ this.instances.clear();
114
+ }
115
+ /**
116
+ * 注册适配器
117
+ */
118
+ register(entry) {
119
+ this.adapters.set(entry.meta.id, entry);
120
+ }
121
+ /**
122
+ * 批量注册
123
+ */
124
+ registerAll(entries) {
125
+ entries.forEach((entry) => this.register(entry));
126
+ }
127
+ /**
128
+ * 获取适配器实例
129
+ */
130
+ async get(platformId) {
131
+ if (this.instances.has(platformId)) {
132
+ return this.instances.get(platformId);
133
+ }
134
+ const entry = this.adapters.get(platformId);
135
+ if (!entry) {
136
+ return null;
137
+ }
138
+ if (!this.runtime) {
139
+ throw new Error("Runtime not set. Call setRuntime() first.");
140
+ }
141
+ const adapter = entry.factory(this.runtime);
142
+ await adapter.init(this.runtime);
143
+ this.instances.set(platformId, adapter);
144
+ return adapter;
145
+ }
146
+ /**
147
+ * 获取所有平台元信息
148
+ */
149
+ getAllMeta() {
150
+ return Array.from(this.adapters.values()).map((entry) => entry.meta);
151
+ }
152
+ /**
153
+ * 检查平台是否已注册
154
+ */
155
+ has(platformId) {
156
+ return this.adapters.has(platformId);
157
+ }
158
+ /**
159
+ * 获取已注册的平台 ID 列表
160
+ */
161
+ getRegisteredIds() {
162
+ return Array.from(this.adapters.keys());
163
+ }
164
+ /**
165
+ * 清空注册
166
+ */
167
+ clear() {
168
+ this.adapters.clear();
169
+ this.instances.clear();
170
+ }
171
+ /**
172
+ * 获取平台的预处理配置
173
+ */
174
+ getPreprocessConfig(platformId) {
175
+ const entry = this.adapters.get(platformId);
176
+ return {
177
+ ...DEFAULT_PREPROCESS_CONFIG,
178
+ ...entry?.preprocessConfig || {}
179
+ };
180
+ }
181
+ /**
182
+ * 获取多个平台的预处理配置
183
+ */
184
+ getPreprocessConfigs(platformIds) {
185
+ const configs = {};
186
+ for (const id of platformIds) {
187
+ configs[id] = this.getPreprocessConfig(id);
188
+ }
189
+ return configs;
190
+ }
191
+ };
192
+ var adapterRegistry = new AdapterRegistry();
193
+ function registerAdapter(entry) {
194
+ adapterRegistry.register(entry);
195
+ }
196
+ async function getAdapter(platformId) {
197
+ return adapterRegistry.get(platformId);
198
+ }
199
+ function getPreprocessConfig(platformId) {
200
+ return adapterRegistry.getPreprocessConfig(platformId);
201
+ }
202
+ function getPreprocessConfigs(platformIds) {
203
+ return adapterRegistry.getPreprocessConfigs(platformIds);
204
+ }
205
+
206
+ // src/adapters/platforms/douban.ts
207
+ var logger = createLogger("Douban");
208
+ var DoubanAdapter = class extends CodeAdapter {
209
+ meta = {
210
+ id: "douban",
211
+ name: "\u8C46\u74E3",
212
+ icon: "https://www.douban.com/favicon.ico",
213
+ homepage: "https://www.douban.com/note/create",
214
+ capabilities: ["article", "draft", "image_upload"]
215
+ };
216
+ /** 预处理配置: 豆瓣使用 Markdown 格式 (转换为 Draft.js) */
217
+ preprocessConfig = {
218
+ outputFormat: "markdown"
219
+ };
220
+ username = "";
221
+ avatar = "";
222
+ formData = null;
223
+ postParams = null;
224
+ /** 豆瓣 API 需要的 Header 规则 */
225
+ HEADER_RULES = [
226
+ {
227
+ urlFilter: "*://www.douban.com/*",
228
+ headers: {
229
+ "Origin": "https://www.douban.com",
230
+ "Referer": "https://www.douban.com"
231
+ },
232
+ resourceTypes: ["xmlhttprequest"]
233
+ }
234
+ ];
235
+ async checkAuth() {
236
+ try {
237
+ const response = await this.runtime.fetch(
238
+ "https://www.douban.com/note/create",
239
+ {
240
+ method: "GET",
241
+ credentials: "include"
242
+ }
243
+ );
244
+ const html = await response.text();
245
+ const userNameMatch = html.match(/_USER_NAME\s*=\s*['"]([^'"]+)['"]/);
246
+ const userAvatarMatch = html.match(/_USER_AVATAR\s*=\s*['"]([^'"]+)['"]/);
247
+ const noteIdMatch = html.match(/name="note_id"\s+value="(\d+)"/);
248
+ const ckMatch = html.match(/name="ck"\s+value="([^"]+)"/);
249
+ const postParamsMatch = html.match(/_POST_PARAMS\s*=\s*(\{[\s\S]*?\});/);
250
+ if (!userNameMatch || !noteIdMatch || !ckMatch) {
251
+ return { isAuthenticated: false };
252
+ }
253
+ this.username = userNameMatch[1];
254
+ this.avatar = userAvatarMatch ? userAvatarMatch[1] : "";
255
+ this.formData = {
256
+ note_id: noteIdMatch[1],
257
+ ck: ckMatch[1]
258
+ };
259
+ if (postParamsMatch) {
260
+ try {
261
+ const siteCookieMatch = postParamsMatch[1].match(/siteCookie[^}]*value\s*:\s*['"]([^'"]+)['"]/);
262
+ if (siteCookieMatch) {
263
+ this.postParams = {
264
+ siteCookie: { value: siteCookieMatch[1] }
265
+ };
266
+ }
267
+ } catch (e) {
268
+ logger.warn("Failed to parse _POST_PARAMS:", e);
269
+ }
270
+ }
271
+ logger.debug("Auth info:", {
272
+ username: this.username,
273
+ noteId: this.formData.note_id,
274
+ hasPostParams: !!this.postParams
275
+ });
276
+ return {
277
+ isAuthenticated: true,
278
+ userId: this.username,
279
+ username: this.username,
280
+ avatar: this.avatar
281
+ };
282
+ } catch (error) {
283
+ logger.debug("checkAuth: not logged in -", error);
284
+ return { isAuthenticated: false, error: error.message };
285
+ }
286
+ }
287
+ async publish(article, options) {
288
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
289
+ logger.info("Starting publish...");
290
+ if (!this.formData) {
291
+ const auth = await this.checkAuth();
292
+ if (!auth.isAuthenticated) {
293
+ throw new Error("\u8BF7\u5148\u767B\u5F55\u8C46\u74E3");
294
+ }
295
+ }
296
+ let content = article.markdown || "";
297
+ const imageDataMap = /* @__PURE__ */ new Map();
298
+ content = await this.processImages(
299
+ content,
300
+ async (src) => {
301
+ const result = await this.uploadImageWithFullData(src);
302
+ imageDataMap.set(result.url, result.imageData);
303
+ return result;
304
+ },
305
+ {
306
+ skipPatterns: ["doubanio.com", "douban.com"],
307
+ onProgress: options?.onImageProgress
308
+ }
309
+ );
310
+ const draftContent = markdownToDraft(content, imageDataMap);
311
+ const response = await this.runtime.fetch(
312
+ "https://www.douban.com/j/note/autosave",
313
+ {
314
+ method: "POST",
315
+ credentials: "include",
316
+ headers: {
317
+ "Content-Type": "application/x-www-form-urlencoded"
318
+ },
319
+ body: new URLSearchParams({
320
+ is_rich: "1",
321
+ note_id: this.formData.note_id,
322
+ note_title: article.title,
323
+ note_text: draftContent,
324
+ introduction: "",
325
+ note_privacy: "P",
326
+ cannot_reply: "",
327
+ author_tags: "",
328
+ accept_donation: "",
329
+ donation_notice: "",
330
+ is_original: "",
331
+ ck: this.formData.ck
332
+ })
333
+ }
334
+ );
335
+ const res = await response.json();
336
+ logger.debug("Save response:", res);
337
+ const draftUrl = "https://www.douban.com/note/create";
338
+ return this.createResult(true, {
339
+ postId: this.formData.note_id,
340
+ postUrl: draftUrl
341
+ });
342
+ }).catch((error) => this.createResult(false, {
343
+ error: error.message
344
+ }));
345
+ }
346
+ /**
347
+ * 上传图片并返回完整数据
348
+ */
349
+ async uploadImageWithFullData(src) {
350
+ if (!this.formData || !this.postParams) {
351
+ throw new Error("\u672A\u83B7\u53D6\u4E0A\u4F20\u51ED\u8BC1");
352
+ }
353
+ const imageResponse = await fetch(src);
354
+ if (!imageResponse.ok) {
355
+ throw new Error("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25: " + src);
356
+ }
357
+ const imageBlob = await imageResponse.blob();
358
+ const formData = new FormData();
359
+ formData.append("note_id", this.formData.note_id);
360
+ formData.append("image_file", imageBlob, "image.jpg");
361
+ formData.append("ck", this.formData.ck);
362
+ formData.append("upload_auth_token", this.postParams.siteCookie.value);
363
+ const uploadResponse = await this.runtime.fetch(
364
+ "https://www.douban.com/j/note/add_photo",
365
+ {
366
+ method: "POST",
367
+ credentials: "include",
368
+ body: formData
369
+ }
370
+ );
371
+ const res = await uploadResponse.json();
372
+ logger.debug("Image upload response:", res);
373
+ if (!res.photo?.url) {
374
+ throw new Error("\u56FE\u7247\u4E0A\u4F20\u5931\u8D25");
375
+ }
376
+ const photo = res.photo;
377
+ return {
378
+ url: photo.url,
379
+ imageData: {
380
+ id: photo.id,
381
+ url: photo.url,
382
+ thumb: photo.thumb,
383
+ width: photo.width,
384
+ height: photo.height,
385
+ file_name: photo.file_name,
386
+ file_size: photo.file_size
387
+ }
388
+ };
389
+ }
390
+ };
391
+
392
+ // src/adapters/platforms/xueqiu.ts
393
+ import { Remarkable } from "remarkable";
394
+ var logger2 = createLogger("Xueqiu");
395
+ var XueqiuAdapter = class extends CodeAdapter {
396
+ meta = {
397
+ id: "xueqiu",
398
+ name: "\u96EA\u7403",
399
+ icon: "https://xqdoc.imedao.com/17aebcfb84a145d33fc18679.ico",
400
+ homepage: "https://mp.xueqiu.com/writeV2",
401
+ capabilities: ["article", "draft", "image_upload"]
402
+ };
403
+ /** 预处理配置: 雪球使用 Markdown 格式 */
404
+ preprocessConfig = {
405
+ outputFormat: "markdown",
406
+ // doPreFilter + processDocCode (旧版)
407
+ removeSpecialTags: true,
408
+ removeSpecialTagsWithParent: true,
409
+ processCodeBlocks: true
410
+ };
411
+ currentUser = null;
412
+ /** 雪球 API 需要的 Header 规则 */
413
+ HEADER_RULES = [
414
+ {
415
+ urlFilter: "*://mp.xueqiu.com/xq/*",
416
+ headers: {
417
+ "Origin": "https://mp.xueqiu.com",
418
+ "Referer": "https://mp.xueqiu.com/"
419
+ },
420
+ resourceTypes: ["xmlhttprequest"]
421
+ }
422
+ ];
423
+ async checkAuth() {
424
+ try {
425
+ const response = await this.runtime.fetch(
426
+ "https://mp.xueqiu.com/writeV2",
427
+ {
428
+ method: "GET",
429
+ credentials: "include"
430
+ }
431
+ );
432
+ const html = await response.text();
433
+ const userMatch = html.match(/window\.UOM_CURRENTUSER\s*=\s*(\{[\s\S]*?\})\s*<\/script>/);
434
+ if (!userMatch) {
435
+ return { isAuthenticated: false };
436
+ }
437
+ try {
438
+ const state = JSON.parse(userMatch[1]);
439
+ const { currentUser } = state;
440
+ if (!currentUser?.id) {
441
+ return { isAuthenticated: false };
442
+ }
443
+ this.currentUser = currentUser;
444
+ const avatar = currentUser.photo_domain && currentUser.profile_image_url ? `https:${currentUser.photo_domain}${currentUser.profile_image_url.split(",")[0]}` : "";
445
+ return {
446
+ isAuthenticated: true,
447
+ userId: String(currentUser.id),
448
+ username: currentUser.screen_name,
449
+ avatar
450
+ };
451
+ } catch (e) {
452
+ logger2.error(" Failed to parse user data:", e);
453
+ return { isAuthenticated: false };
454
+ }
455
+ } catch (error) {
456
+ logger2.debug("checkAuth: not logged in -", error);
457
+ return { isAuthenticated: false, error: error.message };
458
+ }
459
+ }
460
+ async publish(article, options) {
461
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
462
+ logger2.info("Starting publish...");
463
+ if (!this.currentUser) {
464
+ const auth = await this.checkAuth();
465
+ if (!auth.isAuthenticated) {
466
+ throw new Error("\u8BF7\u5148\u767B\u5F55\u96EA\u7403");
467
+ }
468
+ }
469
+ let markdown = article.markdown || "";
470
+ markdown = await this.processImages(
471
+ markdown,
472
+ (src) => this.uploadImageByUrl(src),
473
+ {
474
+ skipPatterns: ["xueqiu.com", "imedao.com"],
475
+ onProgress: options?.onImageProgress
476
+ }
477
+ );
478
+ const md = new Remarkable({
479
+ html: true,
480
+ breaks: true
481
+ });
482
+ md.renderer.rules.heading_open = () => "<h4>";
483
+ md.renderer.rules.heading_close = () => "</h4>";
484
+ md.renderer.rules.strong_open = () => "<b>";
485
+ md.renderer.rules.strong_close = () => "</b>";
486
+ md.renderer.rules.em_open = () => "<i>";
487
+ md.renderer.rules.em_close = () => "</i>";
488
+ md.renderer.rules.bullet_list_open = () => "";
489
+ md.renderer.rules.bullet_list_close = () => "";
490
+ md.renderer.rules.ordered_list_open = () => "";
491
+ md.renderer.rules.ordered_list_close = () => "";
492
+ md.renderer.rules.list_item_open = () => "";
493
+ md.renderer.rules.list_item_close = () => "";
494
+ md.renderer.rules.hr = () => "";
495
+ md.renderer.rules.image = (tokens, idx) => {
496
+ const src = tokens[idx].src || "";
497
+ const alt = tokens[idx].alt || "";
498
+ return `<img src="${src}" alt="${alt}" class="ke_img">`;
499
+ };
500
+ let rendered = md.render(markdown);
501
+ rendered = rendered.replace(/<p>\s*<\/p>/g, "").replace(/\n{3,}/g, "\n\n").trim();
502
+ const content = rendered;
503
+ const formData = new URLSearchParams({
504
+ text: content,
505
+ title: article.title,
506
+ cover_pic: "",
507
+ flags: "false",
508
+ original_event: "",
509
+ status_id: "",
510
+ legal_user_visible: "false",
511
+ is_private: "false"
512
+ });
513
+ const response = await this.runtime.fetch(
514
+ "https://mp.xueqiu.com/xq/statuses/draft/save.json",
515
+ {
516
+ method: "POST",
517
+ credentials: "include",
518
+ headers: {
519
+ "Content-Type": "application/x-www-form-urlencoded"
520
+ },
521
+ body: formData
522
+ }
523
+ );
524
+ const res = await response.json();
525
+ logger2.debug(" Save response:", res);
526
+ if (!res.id) {
527
+ throw new Error(res.error_description || "\u4FDD\u5B58\u5931\u8D25");
528
+ }
529
+ const postId = res.id;
530
+ const draftUrl = `https://mp.xueqiu.com/write/draft/${postId}`;
531
+ return this.createResult(true, {
532
+ postId: String(postId),
533
+ postUrl: draftUrl
534
+ });
535
+ }).catch((error) => this.createResult(false, {
536
+ error: error.message
537
+ }));
538
+ }
539
+ /**
540
+ * 通过 URL 上传图片
541
+ */
542
+ async uploadImageByUrl(src) {
543
+ const imageResponse = await fetch(src);
544
+ if (!imageResponse.ok) {
545
+ throw new Error("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25: " + src);
546
+ }
547
+ const imageBlob = await imageResponse.blob();
548
+ const formData = new FormData();
549
+ formData.append("file", imageBlob, "image.jpg");
550
+ const uploadResponse = await this.runtime.fetch(
551
+ "https://mp.xueqiu.com/xq/photo/upload.json",
552
+ {
553
+ method: "POST",
554
+ credentials: "include",
555
+ body: formData
556
+ }
557
+ );
558
+ const res = await uploadResponse.json();
559
+ logger2.debug(" Image upload response:", res);
560
+ if (!res.url || !res.filename) {
561
+ throw new Error("\u56FE\u7247\u4E0A\u4F20\u5931\u8D25");
562
+ }
563
+ const fullUrl = res.url.startsWith("//") ? `https:${res.url}/${res.filename}` : `${res.url}/${res.filename}`;
564
+ return {
565
+ url: fullUrl
566
+ };
567
+ }
568
+ };
569
+
570
+ // src/adapters/platforms/woshipm.ts
571
+ var logger3 = createLogger("Woshipm");
572
+ var WoshipmAdapter = class extends CodeAdapter {
573
+ meta = {
574
+ id: "woshipm",
575
+ name: "\u4EBA\u4EBA\u90FD\u662F\u4EA7\u54C1\u7ECF\u7406",
576
+ icon: "https://www.woshipm.com/favicon.ico",
577
+ homepage: "https://www.woshipm.com",
578
+ capabilities: ["article", "draft", "image_upload"]
579
+ };
580
+ /** 预处理配置: 人人都是产品经理使用 HTML 格式 */
581
+ preprocessConfig = {
582
+ outputFormat: "html",
583
+ removeEmptyLines: true
584
+ };
585
+ jltoken = "";
586
+ /** 人人都是产品经理 API 需要的 Header 规则 */
587
+ HEADER_RULES = [
588
+ {
589
+ urlFilter: "*://woshipm.com/wp-admin/admin-ajax.php*",
590
+ headers: { "X-Requested-With": "XMLHttpRequest" },
591
+ resourceTypes: ["xmlhttprequest"]
592
+ },
593
+ {
594
+ urlFilter: "*://woshipm.com/api2/*",
595
+ headers: { "X-Requested-With": "XMLHttpRequest" },
596
+ resourceTypes: ["xmlhttprequest"]
597
+ },
598
+ {
599
+ urlFilter: "*://woshipm.com/tensorflow/upyun/upload*",
600
+ headers: { "X-Requested-With": "XMLHttpRequest" },
601
+ resourceTypes: ["xmlhttprequest"]
602
+ }
603
+ ];
604
+ async checkAuth() {
605
+ try {
606
+ const pageResponse = await this.runtime.fetch("https://www.woshipm.com/writing", {
607
+ method: "GET",
608
+ credentials: "include"
609
+ });
610
+ const pageText = await pageResponse.text();
611
+ const jltokenMatch = pageText.match(/"jltoken"\s*:\s*"([^"]+)"/);
612
+ if (jltokenMatch) {
613
+ this.jltoken = jltokenMatch[1];
614
+ logger3.debug("Found jltoken");
615
+ }
616
+ const uidMatch = pageText.match(/var\s+userSettings\s*=\s*\{[^}]*"uid"\s*:\s*"(\d+)"/);
617
+ if (!uidMatch) {
618
+ return { isAuthenticated: false };
619
+ }
620
+ const uid = uidMatch[1];
621
+ const response = await this.runtime.fetch(
622
+ `https://www.woshipm.com/api2/user/profile?uid=${uid}`,
623
+ {
624
+ method: "GET",
625
+ credentials: "include",
626
+ headers: {
627
+ "X-Requested-With": "XMLHttpRequest"
628
+ }
629
+ }
630
+ );
631
+ const data = await response.json();
632
+ if (data.CODE === 200 && data.RESULT?.userInfoVo?.uid) {
633
+ return {
634
+ isAuthenticated: true,
635
+ userId: String(data.RESULT.userInfoVo.uid),
636
+ username: data.RESULT.userInfoVo.nickName,
637
+ avatar: data.RESULT.userInfoVo.avartar
638
+ };
639
+ }
640
+ return { isAuthenticated: false };
641
+ } catch (error) {
642
+ logger3.debug("checkAuth: not logged in -", error);
643
+ return { isAuthenticated: false, error: error.message };
644
+ }
645
+ }
646
+ async publish(article, options) {
647
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
648
+ logger3.info("Starting publish...");
649
+ let content = article.html || "";
650
+ content = await this.processImages(
651
+ content,
652
+ (src) => this.uploadImageByUrl(src),
653
+ {
654
+ skipPatterns: ["woshipm.com", "image.woshipm.com"],
655
+ onProgress: options?.onImageProgress
656
+ }
657
+ );
658
+ const createResponse = await this.runtime.fetch(
659
+ "https://www.woshipm.com/wp-admin/admin-ajax.php",
660
+ {
661
+ method: "POST",
662
+ credentials: "include",
663
+ headers: {
664
+ "Content-Type": "application/x-www-form-urlencoded",
665
+ "X-Requested-With": "XMLHttpRequest"
666
+ },
667
+ body: new URLSearchParams({
668
+ action: "add_draft",
669
+ post_title: article.title,
670
+ post_content: content
671
+ })
672
+ }
673
+ );
674
+ const responseText = await createResponse.text();
675
+ logger3.debug("Create draft response:", createResponse.status, responseText.substring(0, 300));
676
+ if (!createResponse.ok) {
677
+ throw new Error(`\u521B\u5EFA\u8349\u7A3F\u5931\u8D25: ${createResponse.status} - ${responseText}`);
678
+ }
679
+ let createData;
680
+ try {
681
+ createData = JSON.parse(responseText);
682
+ } catch {
683
+ throw new Error(`\u521B\u5EFA\u8349\u7A3F\u5931\u8D25: \u54CD\u5E94\u4E0D\u662F\u6709\u6548 JSON - ${responseText.substring(0, 100)}`);
684
+ }
685
+ if (!createData.post_id) {
686
+ throw new Error(createData.error || "\u521B\u5EFA\u8349\u7A3F\u5931\u8D25: \u65E0\u6548\u54CD\u5E94");
687
+ }
688
+ const draftId = String(createData.post_id);
689
+ const draftUrl = createData.url || `https://www.woshipm.com/writing?pid=${draftId}`;
690
+ logger3.debug("Draft created:", draftId);
691
+ return this.createResult(true, {
692
+ postId: draftId,
693
+ postUrl: draftUrl
694
+ });
695
+ }).catch((error) => this.createResult(false, {
696
+ error: error.message
697
+ }));
698
+ }
699
+ /**
700
+ * 通过 Blob 上传图片(覆盖基类方法)
701
+ */
702
+ async uploadImage(file, filename) {
703
+ return this.uploadImageBinaryInternal(file, filename || "image.png");
704
+ }
705
+ /**
706
+ * 通过 URL 上传图片
707
+ */
708
+ async uploadImageByUrl(src) {
709
+ try {
710
+ const imageResponse = await this.runtime.fetch(src, {
711
+ credentials: "omit"
712
+ });
713
+ if (!imageResponse.ok) {
714
+ throw new Error(`Failed to fetch image: ${imageResponse.status}`);
715
+ }
716
+ const blob = await imageResponse.blob();
717
+ const url = await this.uploadImageBinaryInternal(blob, this.getFilenameFromUrl(src));
718
+ return { url };
719
+ } catch (error) {
720
+ logger3.warn("Failed to upload image by URL:", src, error);
721
+ return { url: src };
722
+ }
723
+ }
724
+ /**
725
+ * 上传图片 (二进制方式) - 内部使用
726
+ */
727
+ async uploadImageBinaryInternal(file, filename) {
728
+ const formData = new FormData();
729
+ formData.append("action", "wpuf_insert_image");
730
+ formData.append("name", filename);
731
+ formData.append("files", file, filename);
732
+ const headers = {
733
+ "Origin": "https://www.woshipm.com",
734
+ "Referer": "https://www.woshipm.com/writing"
735
+ };
736
+ if (this.jltoken) {
737
+ headers["jlstar"] = `Bearer ${this.jltoken}`;
738
+ }
739
+ const response = await this.runtime.fetch("https://www.woshipm.com/tensorflow/upyun/upload", {
740
+ method: "POST",
741
+ credentials: "include",
742
+ headers,
743
+ body: formData
744
+ });
745
+ const data = await response.json();
746
+ if (data.data && data.data.length > 0 && data.data[0].url) {
747
+ logger3.debug("Uploaded image:", filename, "->", data.data[0].url);
748
+ return data.data[0].url;
749
+ }
750
+ throw new Error(data.error || "Failed to upload image");
751
+ }
752
+ /**
753
+ * 从 URL 提取文件名
754
+ */
755
+ getFilenameFromUrl(url) {
756
+ try {
757
+ const pathname = new URL(url).pathname;
758
+ const filename = pathname.split("/").pop() || "image.png";
759
+ return filename;
760
+ } catch {
761
+ return "image.png";
762
+ }
763
+ }
764
+ };
765
+
766
+ // src/adapters/platforms/yuque.ts
767
+ var logger4 = createLogger("Yuque");
768
+ var YuqueAdapter = class extends CodeAdapter {
769
+ meta = {
770
+ id: "yuque",
771
+ name: "\u8BED\u96C0",
772
+ icon: "https://gw.alipayobjects.com/zos/rmsportal/UTjFYEzMSYVwzxIGVhMu.png",
773
+ homepage: "https://www.yuque.com/dashboard",
774
+ capabilities: ["article", "draft", "image_upload"]
775
+ };
776
+ /** 预处理配置: 语雀使用 Markdown 格式 (转换为 lake) */
777
+ preprocessConfig = {
778
+ outputFormat: "markdown",
779
+ // doPreFilter + processDocCode (旧版)
780
+ removeSpecialTags: true,
781
+ removeSpecialTagsWithParent: true,
782
+ processCodeBlocks: true
783
+ };
784
+ userInfo = null;
785
+ bookId = null;
786
+ csrfToken = "";
787
+ currentPostId = null;
788
+ /** 语雀 API 需要的 Header 规则 */
789
+ HEADER_RULES = [
790
+ {
791
+ urlFilter: "*://www.yuque.com/api/*",
792
+ headers: {
793
+ "Origin": "https://www.yuque.com",
794
+ "Referer": "https://www.yuque.com/dashboard"
795
+ },
796
+ resourceTypes: ["xmlhttprequest"]
797
+ }
798
+ ];
799
+ async getCsrfToken() {
800
+ if (this.runtime.getCookie) {
801
+ const value = await this.runtime.getCookie(".yuque.com", "yuque_ctoken");
802
+ if (!value) {
803
+ throw new Error("\u8BF7\u5148\u767B\u5F55\u8BED\u96C0");
804
+ }
805
+ return value;
806
+ }
807
+ throw new Error("\u8BF7\u5148\u767B\u5F55\u8BED\u96C0");
808
+ }
809
+ async checkAuth() {
810
+ try {
811
+ this.csrfToken = await this.getCsrfToken();
812
+ const response = await this.runtime.fetch(
813
+ "https://www.yuque.com/api/mine/common_used",
814
+ {
815
+ method: "GET",
816
+ credentials: "include",
817
+ headers: {
818
+ "x-csrf-token": this.csrfToken
819
+ }
820
+ }
821
+ );
822
+ const res = await response.json();
823
+ logger4.debug("checkAuth response:", res);
824
+ if (res.data?.books && res.data.books.length > 0) {
825
+ const firstBook = res.data.books[0];
826
+ this.userInfo = firstBook.user;
827
+ this.bookId = firstBook.target_id;
828
+ return {
829
+ isAuthenticated: true,
830
+ userId: String(firstBook.user.id),
831
+ username: firstBook.user.name,
832
+ avatar: firstBook.user.avatar_url
833
+ };
834
+ }
835
+ return { isAuthenticated: false };
836
+ } catch (error) {
837
+ logger4.debug("checkAuth: not logged in -", error.message);
838
+ return { isAuthenticated: false, error: error.message };
839
+ }
840
+ }
841
+ async publish(article, options) {
842
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
843
+ logger4.info("Starting publish...");
844
+ if (!this.userInfo || !this.bookId) {
845
+ const auth = await this.checkAuth();
846
+ if (!auth.isAuthenticated) {
847
+ throw new Error("\u8BF7\u5148\u767B\u5F55\u8BED\u96C0");
848
+ }
849
+ }
850
+ const createResponse = await this.runtime.fetch(
851
+ "https://www.yuque.com/api/docs",
852
+ {
853
+ method: "POST",
854
+ credentials: "include",
855
+ headers: {
856
+ "Content-Type": "application/json",
857
+ "x-csrf-token": this.csrfToken
858
+ },
859
+ body: JSON.stringify({
860
+ title: article.title,
861
+ type: "Doc",
862
+ format: "lake",
863
+ book_id: this.bookId,
864
+ status: 0
865
+ })
866
+ }
867
+ );
868
+ const createRes = await createResponse.json();
869
+ logger4.debug("Create doc response:", createRes);
870
+ if (!createRes.data?.id) {
871
+ throw new Error(createRes.message || "\u521B\u5EFA\u6587\u6863\u5931\u8D25");
872
+ }
873
+ const postId = createRes.data.id;
874
+ this.currentPostId = postId;
875
+ let markdown = article.markdown || "";
876
+ markdown = await this.processImages(
877
+ markdown,
878
+ (src) => this.uploadImageByUrl(src),
879
+ {
880
+ skipPatterns: ["yuque.com", "cdn.nlark.com"],
881
+ onProgress: options?.onImageProgress
882
+ }
883
+ );
884
+ const convertResponse = await this.runtime.fetch(
885
+ "https://www.yuque.com/api/docs/convert",
886
+ {
887
+ method: "POST",
888
+ credentials: "include",
889
+ headers: {
890
+ "Content-Type": "application/json",
891
+ "x-csrf-token": this.csrfToken
892
+ },
893
+ body: JSON.stringify({
894
+ from: "markdown",
895
+ to: "lake",
896
+ content: markdown
897
+ })
898
+ }
899
+ );
900
+ const convertRes = await convertResponse.json();
901
+ if (!convertRes.data?.content) {
902
+ throw new Error("\u5185\u5BB9\u8F6C\u6362\u5931\u8D25");
903
+ }
904
+ const lakeContent = convertRes.data.content;
905
+ const saveResponse = await this.runtime.fetch(
906
+ `https://www.yuque.com/api/docs/${postId}/content`,
907
+ {
908
+ method: "PUT",
909
+ credentials: "include",
910
+ headers: {
911
+ "Content-Type": "application/json",
912
+ "x-csrf-token": this.csrfToken
913
+ },
914
+ body: JSON.stringify({
915
+ format: "lake",
916
+ body_asl: lakeContent,
917
+ body: `<div class="lake-content" typography="traditional">${lakeContent}</div>`,
918
+ body_html: `<div class="lake-content" typography="traditional">${lakeContent}</div>`,
919
+ draft_version: 0,
920
+ sync_dynamic_data: false,
921
+ save_type: "auto",
922
+ edit_type: "Lake"
923
+ })
924
+ }
925
+ );
926
+ const saveRes = await saveResponse.json();
927
+ logger4.debug("Save response:", saveRes);
928
+ const draftUrl = `https://www.yuque.com/go/doc/${postId}/edit`;
929
+ return this.createResult(true, {
930
+ postId: String(postId),
931
+ postUrl: draftUrl
932
+ });
933
+ }).catch((error) => this.createResult(false, {
934
+ error: error.message
935
+ }));
936
+ }
937
+ async uploadImageByUrl(src) {
938
+ if (!this.currentPostId) {
939
+ throw new Error("\u6587\u6863 ID \u672A\u8BBE\u7F6E");
940
+ }
941
+ const imageResponse = await fetch(src);
942
+ if (!imageResponse.ok) {
943
+ throw new Error("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25: " + src);
944
+ }
945
+ const imageBlob = await imageResponse.blob();
946
+ const formData = new FormData();
947
+ formData.append("file", imageBlob, "image.jpg");
948
+ const uploadUrl = `https://www.yuque.com/api/upload/attach?attachable_type=Doc&attachable_id=${this.currentPostId}&type=image`;
949
+ const uploadResponse = await this.runtime.fetch(uploadUrl, {
950
+ method: "POST",
951
+ credentials: "include",
952
+ headers: {
953
+ "x-csrf-token": this.csrfToken
954
+ },
955
+ body: formData
956
+ });
957
+ const res = await uploadResponse.json();
958
+ logger4.debug("Image upload response:", res);
959
+ if (!res.data?.url) {
960
+ throw new Error("\u56FE\u7247\u4E0A\u4F20\u5931\u8D25");
961
+ }
962
+ return {
963
+ url: res.data.url
964
+ };
965
+ }
966
+ };
967
+
968
+ // src/adapters/platforms/imooc.ts
969
+ var ImoocAdapter = class extends CodeAdapter {
970
+ meta = {
971
+ id: "imooc",
972
+ name: "\u6155\u8BFE\u624B\u8BB0",
973
+ icon: "https://www.imooc.com/favicon.ico",
974
+ homepage: "https://www.imooc.com/article",
975
+ capabilities: ["article", "draft", "image_upload"]
976
+ };
977
+ /** 预处理配置: 慕课网使用 Markdown 格式 */
978
+ preprocessConfig = {
979
+ outputFormat: "markdown"
980
+ };
981
+ /** 慕课网 API 需要的 Header 规则 */
982
+ HEADER_RULES = [
983
+ {
984
+ urlFilter: "*://www.imooc.com/article/*",
985
+ headers: {
986
+ Origin: "https://www.imooc.com",
987
+ Referer: "https://www.imooc.com/"
988
+ },
989
+ resourceTypes: ["xmlhttprequest"]
990
+ }
991
+ ];
992
+ /**
993
+ * 检查登录状态
994
+ */
995
+ async checkAuth() {
996
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
997
+ const response = await this.runtime.fetch("https://www.imooc.com/u/card", {
998
+ credentials: "include"
999
+ });
1000
+ let text = await response.text();
1001
+ text = text.replace("jsonpcallback(", "").replace("})", "}");
1002
+ const result = JSON.parse(text);
1003
+ if (result.result !== 0) {
1004
+ return { isAuthenticated: false, error: result.msg || "\u672A\u767B\u5F55" };
1005
+ }
1006
+ return {
1007
+ isAuthenticated: true,
1008
+ userId: result.data.uid,
1009
+ username: result.data.nickname,
1010
+ avatar: result.data.img
1011
+ };
1012
+ }).catch((error) => ({ isAuthenticated: false, error: error.message }));
1013
+ }
1014
+ /**
1015
+ * 上传图片
1016
+ */
1017
+ async uploadImageByUrl(url) {
1018
+ const imageResponse = await this.runtime.fetch(url);
1019
+ const blob = await imageResponse.blob();
1020
+ const formData = new FormData();
1021
+ const filename = `${Date.now()}.jpg`;
1022
+ const file = new File([blob], filename, { type: blob.type || "image/jpeg" });
1023
+ formData.append("photo", file, filename);
1024
+ formData.append("type", file.type);
1025
+ formData.append("id", "WU_FILE_0");
1026
+ formData.append("name", filename);
1027
+ formData.append("lastModifiedDate", (/* @__PURE__ */ new Date()).toString());
1028
+ formData.append("size", String(file.size));
1029
+ const response = await this.runtime.fetch(
1030
+ "https://www.imooc.com/article/ajaxuploadimg",
1031
+ {
1032
+ method: "POST",
1033
+ credentials: "include",
1034
+ body: formData
1035
+ }
1036
+ );
1037
+ const res = await response.json();
1038
+ if (res.result !== 0) {
1039
+ throw new Error(res.msg || "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25");
1040
+ }
1041
+ let imgUrl = res.data.imgpath;
1042
+ if (imgUrl.startsWith("//")) {
1043
+ imgUrl = "https:" + imgUrl;
1044
+ }
1045
+ return { url: imgUrl };
1046
+ }
1047
+ /**
1048
+ * 发布文章
1049
+ */
1050
+ async publish(article) {
1051
+ const now = Date.now();
1052
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
1053
+ let content = article.markdown || article.html || "";
1054
+ content = await this.processImages(content, (src) => this.uploadImageByUrl(src));
1055
+ const response = await this.runtime.fetch("https://www.imooc.com/article/savedraft", {
1056
+ method: "POST",
1057
+ credentials: "include",
1058
+ headers: {
1059
+ "Content-Type": "application/x-www-form-urlencoded"
1060
+ },
1061
+ body: new URLSearchParams({
1062
+ editor: "0",
1063
+ draft_id: "0",
1064
+ title: article.title,
1065
+ content
1066
+ })
1067
+ });
1068
+ const res = await response.json();
1069
+ if (!res.data) {
1070
+ throw new Error("\u53D1\u5E03\u5931\u8D25");
1071
+ }
1072
+ return {
1073
+ platform: this.meta.id,
1074
+ success: true,
1075
+ postId: res.data,
1076
+ postUrl: `https://www.imooc.com/article/draft/id/${res.data}`,
1077
+ timestamp: now
1078
+ };
1079
+ }).catch((error) => ({
1080
+ platform: this.meta.id,
1081
+ success: false,
1082
+ error: error.message,
1083
+ timestamp: now
1084
+ }));
1085
+ }
1086
+ };
1087
+
1088
+ // src/adapters/platforms/oschina.ts
1089
+ var OschinaAdapter = class extends CodeAdapter {
1090
+ meta = {
1091
+ id: "oschina",
1092
+ name: "\u5F00\u6E90\u4E2D\u56FD",
1093
+ icon: "https://www.oschina.net/favicon.ico",
1094
+ homepage: "https://my.oschina.net",
1095
+ capabilities: ["article", "draft", "image_upload"]
1096
+ };
1097
+ /** 预处理配置: 开源中国使用 Markdown 格式 */
1098
+ preprocessConfig = {
1099
+ outputFormat: "markdown"
1100
+ };
1101
+ userId = null;
1102
+ /** 开源中国 API 需要的 Header 规则 */
1103
+ HEADER_RULES = [
1104
+ {
1105
+ urlFilter: "*://apiv1.oschina.net/oschinapi/*",
1106
+ headers: {
1107
+ Origin: "https://my.oschina.net",
1108
+ Referer: "https://my.oschina.net/"
1109
+ },
1110
+ resourceTypes: ["xmlhttprequest"]
1111
+ }
1112
+ ];
1113
+ /**
1114
+ * 检查登录状态
1115
+ */
1116
+ async checkAuth() {
1117
+ try {
1118
+ const response = await this.runtime.fetch("https://apiv1.oschina.net/oschinapi/user/myDetails", {
1119
+ credentials: "include"
1120
+ });
1121
+ const data = await response.json();
1122
+ if (!data.success || !data.result?.userId) {
1123
+ return { isAuthenticated: false, error: "\u672A\u767B\u5F55" };
1124
+ }
1125
+ this.userId = String(data.result.userId);
1126
+ return {
1127
+ isAuthenticated: true,
1128
+ userId: this.userId,
1129
+ username: data.result.userVo?.name || this.userId,
1130
+ avatar: data.result.userVo?.portraitUrl
1131
+ };
1132
+ } catch (error) {
1133
+ return { isAuthenticated: false, error: error.message };
1134
+ }
1135
+ }
1136
+ /**
1137
+ * 上传图片
1138
+ */
1139
+ async uploadImageByUrl(url) {
1140
+ if (!this.userId) {
1141
+ await this.checkAuth();
1142
+ }
1143
+ const imageResponse = await this.runtime.fetch(url);
1144
+ const blob = await imageResponse.blob();
1145
+ const filename = this.getFilenameFromUrl(url) || "image";
1146
+ const formData = new FormData();
1147
+ formData.append("file", blob, filename);
1148
+ const response = await this.runtime.fetch(
1149
+ "https://apiv1.oschina.net/oschinapi/ai/creation/project/uploadDetail",
1150
+ {
1151
+ method: "POST",
1152
+ credentials: "include",
1153
+ body: formData
1154
+ }
1155
+ );
1156
+ const res = await response.json();
1157
+ if (!res.success || !res.result) {
1158
+ throw new Error(res.message || "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25");
1159
+ }
1160
+ return { url: res.result };
1161
+ }
1162
+ getFilenameFromUrl(url) {
1163
+ try {
1164
+ const pathname = new URL(url).pathname;
1165
+ const name = pathname.split("/").pop();
1166
+ return name && name.trim() ? name : null;
1167
+ } catch {
1168
+ return null;
1169
+ }
1170
+ }
1171
+ /**
1172
+ * 发布文章
1173
+ */
1174
+ async publish(article) {
1175
+ const now = Date.now();
1176
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
1177
+ if (!this.userId) {
1178
+ const auth = await this.checkAuth();
1179
+ if (!auth.isAuthenticated) {
1180
+ throw new Error("\u672A\u767B\u5F55");
1181
+ }
1182
+ }
1183
+ const rawMarkdown = article.markdown || "";
1184
+ const rawHtml = article.html || "";
1185
+ const useMarkdown = rawMarkdown.trim().length > 0;
1186
+ let content = useMarkdown ? rawMarkdown : rawHtml;
1187
+ content = await this.processImages(content, (src) => this.uploadImageByUrl(src));
1188
+ const response = await this.runtime.fetch(
1189
+ "https://apiv1.oschina.net/oschinapi/api/draft/save_draft",
1190
+ {
1191
+ method: "POST",
1192
+ credentials: "include",
1193
+ headers: {
1194
+ "Content-Type": "application/json"
1195
+ },
1196
+ body: JSON.stringify({
1197
+ title: article.title,
1198
+ user: Number(this.userId),
1199
+ content,
1200
+ contentType: useMarkdown ? 1 : 2,
1201
+ // 1=markdown, 2=html
1202
+ catalog: 0,
1203
+ originUrl: "",
1204
+ privacy: true,
1205
+ disableComment: false
1206
+ })
1207
+ }
1208
+ );
1209
+ const res = await response.json();
1210
+ if (!res.success || !res.result?.id) {
1211
+ throw new Error(res.message || "\u53D1\u5E03\u5931\u8D25");
1212
+ }
1213
+ const draftId = String(res.result.id);
1214
+ return {
1215
+ platform: this.meta.id,
1216
+ success: true,
1217
+ postId: draftId,
1218
+ postUrl: `https://my.oschina.net/u/${this.userId}/blog/write/draft/${draftId}`,
1219
+ timestamp: now
1220
+ };
1221
+ }).catch((error) => ({
1222
+ platform: this.meta.id,
1223
+ success: false,
1224
+ error: error.message,
1225
+ timestamp: now
1226
+ }));
1227
+ }
1228
+ };
1229
+
1230
+ // src/adapters/platforms/segmentfault.ts
1231
+ var SegmentfaultAdapter = class extends CodeAdapter {
1232
+ meta = {
1233
+ id: "segmentfault",
1234
+ name: "\u601D\u5426",
1235
+ icon: "https://imgcache.iyiou.com/Company/2016-05-11/cf-segmentfault.jpg",
1236
+ homepage: "https://segmentfault.com/user/draft",
1237
+ capabilities: ["article", "draft", "image_upload"]
1238
+ };
1239
+ /** 预处理配置: 思否使用 Markdown 格式 */
1240
+ preprocessConfig = {
1241
+ outputFormat: "markdown"
1242
+ };
1243
+ sessionToken = null;
1244
+ /** 思否 API 需要的 Header 规则 */
1245
+ HEADER_RULES = [
1246
+ {
1247
+ urlFilter: "*://segmentfault.com/gateway/*",
1248
+ headers: {
1249
+ Origin: "https://segmentfault.com",
1250
+ Referer: "https://segmentfault.com/"
1251
+ },
1252
+ resourceTypes: ["xmlhttprequest"]
1253
+ }
1254
+ ];
1255
+ /**
1256
+ * 检查登录状态
1257
+ */
1258
+ async checkAuth() {
1259
+ try {
1260
+ const response = await this.runtime.fetch("https://segmentfault.com/user/settings", {
1261
+ credentials: "include"
1262
+ });
1263
+ const html = await response.text();
1264
+ const userLinkMatch = html.match(/href="\/u\/([^"]+)"/);
1265
+ if (!userLinkMatch) {
1266
+ return { isAuthenticated: false, error: "\u672A\u767B\u5F55" };
1267
+ }
1268
+ const uid = userLinkMatch[1];
1269
+ const avatarMatch = html.match(/src="(https:\/\/avatar-static\.segmentfault\.com\/[^"]+)"/);
1270
+ const avatar = avatarMatch ? avatarMatch[1] : void 0;
1271
+ return {
1272
+ isAuthenticated: true,
1273
+ userId: uid,
1274
+ username: uid,
1275
+ avatar
1276
+ };
1277
+ } catch (error) {
1278
+ return { isAuthenticated: false, error: error.message };
1279
+ }
1280
+ }
1281
+ /**
1282
+ * 获取 session token
1283
+ */
1284
+ async getSessionToken() {
1285
+ const response = await this.runtime.fetch("https://segmentfault.com/write", {
1286
+ credentials: "include"
1287
+ });
1288
+ const html = await response.text();
1289
+ const tokenMatch = html.match(/serverData":\s*\{\s*"Token"\s*:\s*"([^"]+)"/);
1290
+ if (tokenMatch) {
1291
+ return tokenMatch[1];
1292
+ }
1293
+ const markStr = "window.g_initialProps = ";
1294
+ const authIndex = html.indexOf(markStr);
1295
+ if (authIndex === -1) {
1296
+ throw new Error("\u83B7\u53D6 session token \u5931\u8D25");
1297
+ }
1298
+ const endIndex = html.indexOf(";\n </script>", authIndex);
1299
+ if (endIndex === -1) {
1300
+ throw new Error("\u89E3\u6790 session token \u5931\u8D25");
1301
+ }
1302
+ const configStr = html.substring(authIndex + markStr.length, endIndex);
1303
+ try {
1304
+ const config = JSON.parse(configStr);
1305
+ const token = config?.global?.sessionInfo?.key;
1306
+ if (!token) {
1307
+ throw new Error("session token \u4E3A\u7A7A");
1308
+ }
1309
+ return token;
1310
+ } catch (e) {
1311
+ throw new Error("\u89E3\u6790 session token \u5931\u8D25: " + e.message);
1312
+ }
1313
+ }
1314
+ /**
1315
+ * 上传图片
1316
+ */
1317
+ async uploadImageByUrl(url) {
1318
+ if (!this.sessionToken) {
1319
+ throw new Error("\u672A\u83B7\u53D6 token");
1320
+ }
1321
+ const imageResponse = await this.runtime.fetch(url);
1322
+ const blob = await imageResponse.blob();
1323
+ const formData = new FormData();
1324
+ formData.append("image", blob);
1325
+ const response = await this.runtime.fetch(
1326
+ "https://segmentfault.com/gateway/image",
1327
+ {
1328
+ method: "POST",
1329
+ credentials: "include",
1330
+ headers: {
1331
+ token: this.sessionToken
1332
+ },
1333
+ body: formData
1334
+ }
1335
+ );
1336
+ const text = await response.text();
1337
+ if (text === "Unauthorized" || text.includes("\u7981\u8A00") || text.includes("\u9501\u5B9A")) {
1338
+ throw new Error(text === "Unauthorized" ? "\u672A\u6388\u6743" : text);
1339
+ }
1340
+ let res;
1341
+ try {
1342
+ res = JSON.parse(text);
1343
+ } catch {
1344
+ throw new Error("\u56FE\u7247\u4E0A\u4F20\u5931\u8D25: " + text);
1345
+ }
1346
+ const imageUrl = res.result || (Array.isArray(res) ? res[0] === 1 ? null : res[1] || `https://image-static.segmentfault.com/${res[2]}` : null);
1347
+ if (!imageUrl) {
1348
+ throw new Error(Array.isArray(res) ? res[1] || "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25" : "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25");
1349
+ }
1350
+ return { url: imageUrl };
1351
+ }
1352
+ /**
1353
+ * 发布文章
1354
+ */
1355
+ async publish(article) {
1356
+ const now = Date.now();
1357
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
1358
+ this.sessionToken = await this.getSessionToken();
1359
+ let content = article.markdown || article.html || "";
1360
+ content = await this.processImages(content, (src) => this.uploadImageByUrl(src));
1361
+ const postData = {
1362
+ title: article.title,
1363
+ tags: [],
1364
+ text: content,
1365
+ object_id: "",
1366
+ type: "article"
1367
+ };
1368
+ const response = await this.runtime.fetch("https://segmentfault.com/gateway/draft", {
1369
+ method: "POST",
1370
+ credentials: "include",
1371
+ headers: {
1372
+ "Content-Type": "application/json",
1373
+ token: this.sessionToken,
1374
+ accept: "*/*"
1375
+ },
1376
+ body: JSON.stringify(postData)
1377
+ });
1378
+ const text = await response.text();
1379
+ if (text === "Unauthorized" || text.includes("\u7981\u8A00") || text.includes("\u9501\u5B9A")) {
1380
+ throw new Error(text === "Unauthorized" ? "\u672A\u6388\u6743" : text);
1381
+ }
1382
+ let res;
1383
+ try {
1384
+ res = JSON.parse(text);
1385
+ } catch {
1386
+ throw new Error("\u53D1\u5E03\u5931\u8D25: " + text);
1387
+ }
1388
+ if (Array.isArray(res)) {
1389
+ if (res[0] === 1) {
1390
+ throw new Error(res[1] || "\u53D1\u5E03\u5931\u8D25");
1391
+ }
1392
+ const data = res[1];
1393
+ if (data?.id) {
1394
+ return {
1395
+ platform: this.meta.id,
1396
+ success: true,
1397
+ postId: data.id,
1398
+ postUrl: `https://segmentfault.com/write?draftId=${data.id}`,
1399
+ timestamp: now
1400
+ };
1401
+ }
1402
+ }
1403
+ if (!res.id) {
1404
+ const errorMsg = res.message || res.msg || res.error || res.errMsg || JSON.stringify(res);
1405
+ throw new Error(errorMsg);
1406
+ }
1407
+ return {
1408
+ platform: this.meta.id,
1409
+ success: true,
1410
+ postId: res.id,
1411
+ postUrl: `https://segmentfault.com/write?draftId=${res.id}`,
1412
+ timestamp: now
1413
+ };
1414
+ }).catch((error) => ({
1415
+ platform: this.meta.id,
1416
+ success: false,
1417
+ error: error.message,
1418
+ timestamp: now
1419
+ }));
1420
+ }
1421
+ };
1422
+
1423
+ // src/adapters/platforms/zip-download.ts
1424
+ import JSZip from "jszip";
1425
+ var logger5 = createLogger("ZipDownload");
1426
+ var ZipDownloadAdapter = class extends CodeAdapter {
1427
+ meta = {
1428
+ id: "zip-download",
1429
+ name: "Markdown \u538B\u7F29\u5305",
1430
+ icon: "https://cdn-icons-png.flaticon.com/512/337/337946.png",
1431
+ homepage: "",
1432
+ capabilities: ["article"]
1433
+ };
1434
+ /**
1435
+ * 本地导出不需要认证
1436
+ */
1437
+ async checkAuth() {
1438
+ return {
1439
+ isAuthenticated: true,
1440
+ username: "\u672C\u5730\u4E0B\u8F7D"
1441
+ };
1442
+ }
1443
+ /**
1444
+ * 导出为 ZIP 压缩包
1445
+ */
1446
+ async publish(article, options) {
1447
+ try {
1448
+ const zip = new JSZip();
1449
+ const imgFolder = zip.folder("images");
1450
+ let markdown = article.markdown || htmlToMarkdown(article.html || "");
1451
+ if (!markdown.trim()) {
1452
+ return this.createResult(false, {
1453
+ error: "\u6587\u7AE0\u5185\u5BB9\u4E3A\u7A7A"
1454
+ });
1455
+ }
1456
+ const title = article.title || "\u672A\u547D\u540D\u6587\u7AE0";
1457
+ if (!markdown.startsWith("# ")) {
1458
+ markdown = `# ${title}
1459
+
1460
+ ${markdown}`;
1461
+ }
1462
+ const { processedMarkdown, imageCount } = await this.processImagesForZip(
1463
+ markdown,
1464
+ imgFolder,
1465
+ options?.onImageProgress
1466
+ );
1467
+ zip.file("article.md", processedMarkdown);
1468
+ const blob = await zip.generateAsync({
1469
+ type: "blob",
1470
+ compression: "DEFLATE",
1471
+ compressionOptions: { level: 6 }
1472
+ });
1473
+ const filename = this.sanitizeFilename(title) + ".zip";
1474
+ if (!this.runtime.downloads) {
1475
+ return this.createResult(false, {
1476
+ error: "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u4E0B\u8F7D\u529F\u80FD"
1477
+ });
1478
+ }
1479
+ const downloadId = await this.runtime.downloads.download(blob, filename, true);
1480
+ logger5.info(`Download started: ${filename}, id: ${downloadId}`);
1481
+ return this.createResult(true, {
1482
+ postId: String(downloadId),
1483
+ postUrl: "",
1484
+ // 本地下载,无链接
1485
+ message: `\u5DF2\u4E0B\u8F7D ${filename}\uFF08${imageCount} \u5F20\u56FE\u7247\uFF09`
1486
+ });
1487
+ } catch (error) {
1488
+ logger5.error("ZIP download failed:", error);
1489
+ return this.createResult(false, {
1490
+ error: error.message
1491
+ });
1492
+ }
1493
+ }
1494
+ /**
1495
+ * 处理 Markdown 中的图片:下载并添加到 ZIP
1496
+ */
1497
+ async processImagesForZip(markdown, imgFolder, onProgress) {
1498
+ const matches = parseMarkdownImages(markdown);
1499
+ if (matches.length === 0) {
1500
+ return { processedMarkdown: markdown, imageCount: 0 };
1501
+ }
1502
+ logger5.info(`Found ${matches.length} images to process`);
1503
+ const timestamp = Math.floor(Date.now() / 1e3);
1504
+ let processedMarkdown = markdown;
1505
+ let imageIndex = 0;
1506
+ let completed = 0;
1507
+ const urlToFilename = /* @__PURE__ */ new Map();
1508
+ const uniqueUrls = [];
1509
+ for (const { src } of matches) {
1510
+ if (src.startsWith("data:")) {
1511
+ logger5.debug("Skipping data URI image");
1512
+ continue;
1513
+ }
1514
+ if (!urlToFilename.has(src)) {
1515
+ urlToFilename.set(src, "");
1516
+ uniqueUrls.push(src);
1517
+ }
1518
+ }
1519
+ if (uniqueUrls.length === 0) {
1520
+ return { processedMarkdown: markdown, imageCount: 0 };
1521
+ }
1522
+ const downloadOne = async (src) => {
1523
+ try {
1524
+ const response = await this.runtime.fetch(src, {
1525
+ credentials: "omit"
1526
+ });
1527
+ if (!response.ok) {
1528
+ logger5.warn(`Failed to download image: ${src}, status: ${response.status}`);
1529
+ return;
1530
+ }
1531
+ const blob = await response.blob();
1532
+ const ext = this.getImageExtension(src, blob.type);
1533
+ imageIndex++;
1534
+ const filename = `image_${timestamp}_${String(imageIndex).padStart(3, "0")}.${ext}`;
1535
+ imgFolder.file(filename, blob);
1536
+ urlToFilename.set(src, filename);
1537
+ logger5.debug(`Downloaded image: ${filename}`);
1538
+ } catch (error) {
1539
+ logger5.warn(`Failed to download image: ${src}`, error);
1540
+ } finally {
1541
+ completed++;
1542
+ onProgress?.(completed, uniqueUrls.length);
1543
+ }
1544
+ };
1545
+ const runPool = async (limit) => {
1546
+ let cursor = 0;
1547
+ const workers = Array.from({ length: Math.min(limit, uniqueUrls.length) }, async () => {
1548
+ while (cursor < uniqueUrls.length) {
1549
+ const current = cursor;
1550
+ cursor++;
1551
+ await downloadOne(uniqueUrls[current]);
1552
+ }
1553
+ });
1554
+ await Promise.all(workers);
1555
+ };
1556
+ await runPool(4);
1557
+ for (const { full, alt, src } of matches) {
1558
+ const filename = urlToFilename.get(src);
1559
+ if (filename) {
1560
+ processedMarkdown = processedMarkdown.replace(full, `![${alt}](images/${filename})`);
1561
+ }
1562
+ }
1563
+ return {
1564
+ processedMarkdown,
1565
+ imageCount: Array.from(urlToFilename.values()).filter(Boolean).length
1566
+ };
1567
+ }
1568
+ /**
1569
+ * 获取图片扩展名
1570
+ */
1571
+ getImageExtension(url, mimeType) {
1572
+ const mimeMap = {
1573
+ "image/jpeg": "jpg",
1574
+ "image/png": "png",
1575
+ "image/gif": "gif",
1576
+ "image/webp": "webp",
1577
+ "image/svg+xml": "svg",
1578
+ "image/bmp": "bmp",
1579
+ "image/x-icon": "ico"
1580
+ };
1581
+ if (mimeType && mimeMap[mimeType]) {
1582
+ return mimeMap[mimeType];
1583
+ }
1584
+ const urlMatch = url.match(/\.(png|jpe?g|gif|webp|svg|bmp|ico)(?:\?|$)/i);
1585
+ if (urlMatch) {
1586
+ return urlMatch[1].toLowerCase().replace("jpeg", "jpg");
1587
+ }
1588
+ return "png";
1589
+ }
1590
+ /**
1591
+ * 清理文件名,移除非法字符
1592
+ */
1593
+ sanitizeFilename(name) {
1594
+ return name.replace(/[<>:"/\\|?*]/g, "_").replace(/[\x00-\x1f]/g, "").replace(/\.+$/, "").trim().slice(0, 200) || "article";
1595
+ }
1596
+ };
1597
+
1598
+ // src/adapters/platforms/eastmoney.ts
1599
+ var logger6 = createLogger("Eastmoney");
1600
+ var EastmoneyAdapter = class extends CodeAdapter {
1601
+ meta = {
1602
+ id: "eastmoney",
1603
+ name: "\u4E1C\u65B9\u8D22\u5BCC",
1604
+ icon: "https://mp.eastmoney.com/collect/pc_article/favicon.ico",
1605
+ homepage: "https://mp.eastmoney.com",
1606
+ capabilities: ["article", "draft", "image_upload", "cover"]
1607
+ };
1608
+ /** 预处理配置 */
1609
+ preprocessConfig = {
1610
+ outputFormat: "html",
1611
+ removeComments: true,
1612
+ removeSpecialTags: true,
1613
+ processCodeBlocks: true,
1614
+ convertSectionToDiv: true,
1615
+ removeEmptyLines: true,
1616
+ removeEmptyDivs: true,
1617
+ removeNestedEmptyContainers: true,
1618
+ unwrapSingleChildContainers: true,
1619
+ unwrapNestedFigures: true,
1620
+ removeTrailingBr: true,
1621
+ removeDataAttributes: true,
1622
+ removeSrcset: true,
1623
+ removeSizes: true,
1624
+ compactHtml: true
1625
+ };
1626
+ ctoken = "";
1627
+ utoken = "";
1628
+ deviceId = "";
1629
+ /** 获取或生成持久化 deviceId(32位大写 hex) */
1630
+ async getDeviceId() {
1631
+ if (this.deviceId) return this.deviceId;
1632
+ const stored = await this.runtime.storage.get("eastmoney_deviceId");
1633
+ if (stored) {
1634
+ this.deviceId = stored;
1635
+ return this.deviceId;
1636
+ }
1637
+ const bytes = new Uint8Array(16);
1638
+ crypto.getRandomValues(bytes);
1639
+ this.deviceId = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0").toUpperCase()).join("");
1640
+ await this.runtime.storage.set("eastmoney_deviceId", this.deviceId);
1641
+ return this.deviceId;
1642
+ }
1643
+ /** API Header 规则 */
1644
+ HEADER_RULES = [
1645
+ {
1646
+ urlFilter: "*://mp.eastmoney.com/*",
1647
+ headers: {
1648
+ Origin: "https://mp.eastmoney.com",
1649
+ HOST: "emfront.eastmoney.com"
1650
+ },
1651
+ resourceTypes: ["xmlhttprequest"]
1652
+ }
1653
+ ];
1654
+ async checkAuth() {
1655
+ try {
1656
+ await this.fetchToken();
1657
+ const response = await this.runtime.fetch(
1658
+ `https://caifuhaoapi.eastmoney.com/api/v2/getauthorinfo?platform=&ctoken=${this.ctoken}&utoken=${this.utoken}`,
1659
+ {
1660
+ method: "GET",
1661
+ credentials: "include",
1662
+ headers: { "x-requested-with": "fetch" }
1663
+ }
1664
+ );
1665
+ const data = await response.json();
1666
+ if (data.Success === 1 && data.Result?.accountId) {
1667
+ return {
1668
+ isAuthenticated: true,
1669
+ userId: data.Result.accountId,
1670
+ username: data.Result.accountName,
1671
+ avatar: data.Result.portrait
1672
+ };
1673
+ }
1674
+ return { isAuthenticated: false };
1675
+ } catch (error) {
1676
+ logger6.debug("checkAuth: not logged in -", error);
1677
+ return { isAuthenticated: false, error: error.message };
1678
+ }
1679
+ }
1680
+ /** 从 cookie 读取 token */
1681
+ async fetchToken() {
1682
+ if (!this.runtime.getCookie) {
1683
+ throw new Error("Cookie API \u4E0D\u53EF\u7528\uFF0C\u8BF7\u5148\u767B\u5F55\u4E1C\u65B9\u8D22\u5BCC");
1684
+ }
1685
+ const ctoken = await this.runtime.getCookie(".eastmoney.com", "ct");
1686
+ const utoken = await this.runtime.getCookie(".eastmoney.com", "ut");
1687
+ if (!ctoken || !utoken) {
1688
+ throw new Error("\u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8BF7\u5148\u767B\u5F55\u4E1C\u65B9\u8D22\u5BCC");
1689
+ }
1690
+ this.ctoken = ctoken;
1691
+ this.utoken = utoken;
1692
+ }
1693
+ async publish(article, options) {
1694
+ return this.withHeaderRules(this.HEADER_RULES, async () => {
1695
+ await this.fetchToken();
1696
+ logger6.info("Starting publish to eastmoney...");
1697
+ const draftId = await this.createDraft(article.title);
1698
+ logger6.debug("Draft created:", draftId);
1699
+ const content = await this.processImages(
1700
+ article.html || "",
1701
+ (src) => this.uploadImageByUrl(src),
1702
+ {
1703
+ skipPatterns: ["gbres.dfcfw.com"],
1704
+ onProgress: options?.onImageProgress
1705
+ }
1706
+ );
1707
+ await this.updateDraft(draftId, article.title, content);
1708
+ logger6.debug("Draft updated");
1709
+ const draftUrl = `https://mp.eastmoney.com/collect/pc_article/index.html#/?id=${draftId}`;
1710
+ return this.createResult(true, {
1711
+ postId: draftId,
1712
+ postUrl: draftUrl
1713
+ });
1714
+ }).catch(
1715
+ (error) => this.createResult(false, {
1716
+ error: error.message
1717
+ })
1718
+ );
1719
+ }
1720
+ /** 构造 API 参数 */
1721
+ async buildParm(params) {
1722
+ const deviceid = await this.getDeviceId();
1723
+ return [
1724
+ { ip: "$IP$" },
1725
+ { deviceid },
1726
+ { version: "100" },
1727
+ { plat: "web" },
1728
+ { product: "CFH" },
1729
+ { ctoken: this.ctoken },
1730
+ { utoken: this.utoken },
1731
+ { draftid: params.draftid ?? "" },
1732
+ { drafttype: "0" },
1733
+ { type: "0" },
1734
+ { title: encodeURIComponent(params.title) },
1735
+ { text: encodeURIComponent(params.text) },
1736
+ { columns: "2" },
1737
+ { cover: "" },
1738
+ { issimplevideo: "0" },
1739
+ { videos: "" },
1740
+ { vods: "" },
1741
+ { isoriginal: "0" },
1742
+ { tgProduct: "" },
1743
+ { spcolumns: "" },
1744
+ { textsource: "0" },
1745
+ { replyauthority: "" },
1746
+ { modules: encodeURIComponent("[]") }
1747
+ ];
1748
+ }
1749
+ /** 调用草稿 API */
1750
+ async callDraftApi(parm, draftId) {
1751
+ const pageUrl = draftId ? `https://mp.eastmoney.com/collect/pc_article/index.html#/?id=${draftId}` : "https://mp.eastmoney.com/collect/pc_article/index.html#/";
1752
+ const body = JSON.stringify({
1753
+ pageUrl,
1754
+ path: "draft/api/Article/SaveDraft",
1755
+ parm: JSON.stringify(parm)
1756
+ });
1757
+ const response = await this.runtime.fetch(
1758
+ "https://emfront.eastmoney.com/apifront/Tran/GetData?platform=",
1759
+ {
1760
+ method: "POST",
1761
+ credentials: "include",
1762
+ headers: { "Content-Type": "application/json" },
1763
+ body
1764
+ }
1765
+ );
1766
+ const responseText = await response.text();
1767
+ logger6.debug(
1768
+ "Draft API response:",
1769
+ response.status,
1770
+ responseText.substring(0, 200)
1771
+ );
1772
+ if (!response.ok) {
1773
+ throw new Error(`\u8349\u7A3F API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
1774
+ }
1775
+ let rawData;
1776
+ try {
1777
+ rawData = JSON.parse(responseText);
1778
+ } catch {
1779
+ throw new Error("\u8349\u7A3F API \u54CD\u5E94\u4E0D\u662F\u6709\u6548 JSON");
1780
+ }
1781
+ if (!rawData.RRquestSuccess || rawData.RCode !== 200) {
1782
+ throw new Error(`\u8349\u7A3F API \u9519\u8BEF: ${rawData.RMsg || "\u672A\u77E5\u9519\u8BEF"}`);
1783
+ }
1784
+ let innerData;
1785
+ try {
1786
+ innerData = JSON.parse(rawData.RData);
1787
+ } catch {
1788
+ throw new Error("\u65E0\u6CD5\u89E3\u6790\u8349\u7A3F\u54CD\u5E94\u6570\u636E");
1789
+ }
1790
+ if (innerData.error_code !== 0) {
1791
+ throw new Error(`\u8349\u7A3F\u4E1A\u52A1\u9519\u8BEF: ${innerData.me || "\u672A\u77E5\u9519\u8BEF"}`);
1792
+ }
1793
+ return innerData;
1794
+ }
1795
+ async createDraft(title) {
1796
+ const parm = await this.buildParm({
1797
+ title,
1798
+ text: '<div class="xeditor_content cfh_web"></div>'
1799
+ });
1800
+ const result = await this.callDraftApi(parm);
1801
+ if (!result.draft_id) {
1802
+ throw new Error("\u521B\u5EFA\u8349\u7A3F\u5931\u8D25: \u54CD\u5E94\u7F3A\u5C11 draft_id");
1803
+ }
1804
+ return result.draft_id;
1805
+ }
1806
+ async updateDraft(draftId, title, content) {
1807
+ const parm = await this.buildParm({
1808
+ draftid: draftId,
1809
+ title,
1810
+ text: `<div class="xeditor_content cfh_web">${content}</div>`
1811
+ });
1812
+ await this.callDraftApi(parm, draftId);
1813
+ }
1814
+ /** URL 上传图片 */
1815
+ async uploadImageByUrl(src) {
1816
+ if (src.startsWith("data:")) {
1817
+ logger6.debug("Detected data URI, using binary upload");
1818
+ const blob = await this.dataUriToBlob(src);
1819
+ return this.uploadImageBlob(blob);
1820
+ }
1821
+ const response = await this.runtime.fetch(
1822
+ "https://gbapi.eastmoney.com/iimage/image/byLink?platform=",
1823
+ {
1824
+ method: "PUT",
1825
+ credentials: "include",
1826
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1827
+ body: new URLSearchParams({
1828
+ noinlist: "1",
1829
+ linkUrl: src,
1830
+ ctoken: this.ctoken,
1831
+ utoken: this.utoken
1832
+ })
1833
+ }
1834
+ );
1835
+ const res = await response.json();
1836
+ if (res.code === 200 && res.data?.url) {
1837
+ return { url: res.data.url };
1838
+ }
1839
+ throw new Error(
1840
+ `\u56FE\u7247\u4E0A\u4F20\u5931\u8D25: ${res.message || "\u672A\u77E5\u9519\u8BEF"} (code: ${res.code})`
1841
+ );
1842
+ }
1843
+ /** 上传图片 Blob */
1844
+ async uploadImageBlob(file) {
1845
+ const ext = file.type.split("/")[1] || "png";
1846
+ const filename = `${Date.now()}.${ext}`;
1847
+ const formData = new FormData();
1848
+ formData.append("file", file, filename);
1849
+ formData.append("noinlist", "1");
1850
+ formData.append("utoken", this.utoken);
1851
+ formData.append("ctoken", this.ctoken);
1852
+ const response = await this.runtime.fetch(
1853
+ "https://gbapi.eastmoney.com/iimage/image?platform=",
1854
+ {
1855
+ method: "POST",
1856
+ credentials: "include",
1857
+ body: formData
1858
+ }
1859
+ );
1860
+ const res = await response.json();
1861
+ if (res.code === 200 && res.data?.url) {
1862
+ return { url: res.data.url };
1863
+ }
1864
+ throw new Error(
1865
+ `\u56FE\u7247\u4E0A\u4F20\u5931\u8D25: ${res.message || "\u672A\u77E5\u9519\u8BEF"} (code: ${res.code})`
1866
+ );
1867
+ }
1868
+ };
1869
+ export {
1870
+ BaijiahaoAdapter,
1871
+ BaseAdapter,
1872
+ BilibiliAdapter,
1873
+ CSDNAdapter,
1874
+ CnblogsAdapter,
1875
+ CodeAdapter,
1876
+ Cto51Adapter,
1877
+ DEFAULT_PREPROCESS_CONFIG,
1878
+ DoubanAdapter,
1879
+ DouyinAdapter,
1880
+ EastmoneyAdapter,
1881
+ ImoocAdapter,
1882
+ JuejinAdapter,
1883
+ NeteaseAdapter,
1884
+ OschinaAdapter,
1885
+ SegmentfaultAdapter,
1886
+ SohuAdapter,
1887
+ TencentAdapter,
1888
+ ToutiaoAdapter,
1889
+ WeixinAdapter,
1890
+ WoshipmAdapter,
1891
+ XueqiuAdapter,
1892
+ YuqueAdapter,
1893
+ ZipDownloadAdapter,
1894
+ adapterRegistry,
1895
+ getAdapter,
1896
+ getPreprocessConfig,
1897
+ getPreprocessConfigs,
1898
+ registerAdapter
1899
+ };
1900
+ //# sourceMappingURL=index.js.map