@skrillex1224/chrome-article-publish-extension 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/index.js +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/adapters/index.js
CHANGED
|
@@ -1705,7 +1705,7 @@ ${markdown}`;
|
|
|
1705
1705
|
* 清理文件名,移除非法字符
|
|
1706
1706
|
*/
|
|
1707
1707
|
sanitizeFilename(name) {
|
|
1708
|
-
return name.replace(/[<>:"/\\|?*]/g, "_").replace(/[\x00-\x1f]/g, "").replace(/\.+$/, "").trim()
|
|
1708
|
+
return name.replace(/[<>:"/\\|?*]/g, "_").replace(/[\x00-\x1f]/g, "").replace(/\.+$/, "").trim() || "article";
|
|
1709
1709
|
}
|
|
1710
1710
|
};
|
|
1711
1711
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/types.ts","../../src/adapters/base.ts","../../src/adapters/registry.ts","../../src/lib/markdown-to-draft.ts","../../src/adapters/platforms/douban.ts","../../src/adapters/platforms/xueqiu.ts","../../src/adapters/platforms/woshipm.ts","../../src/adapters/platforms/yuque.ts","../../src/adapters/platforms/imooc.ts","../../src/adapters/platforms/oschina.ts","../../src/adapters/platforms/segmentfault.ts","../../src/adapters/platforms/zip-download.ts","../../src/adapters/platforms/eastmoney.ts"],"sourcesContent":["import type {\n Article,\n AuthResult,\n PublishEventHandler,\n PublishStatusRef,\n PublishStatusResult,\n SyncResult,\n PlatformMeta,\n} from '../types'\nimport type { RuntimeInterface } from '../runtime/interface'\n\n/**\n * 输出格式类型\n */\nexport type OutputFormat = 'html' | 'markdown'\n\n/**\n * 预处理配置\n * 每个平台在 adapter 中定义自己需要的预处理选项\n * Content Script 根据这些配置在发送到 Service Worker 前进行预处理\n */\nexport interface PreprocessConfig {\n /** 输出格式: html 或 markdown */\n outputFormat: OutputFormat\n\n /** 移除链接标签,保留文字 */\n removeLinks?: boolean\n /** 保留的链接域名 */\n keepLinkDomains?: string[]\n\n /** 移除 iframe */\n removeIframes?: boolean\n /** 移除 HTML 注释 */\n removeComments?: boolean\n /** 移除微信特殊标签 (mpprofile, qqmusic 等) */\n removeSpecialTags?: boolean\n /** 移除特殊标签时同时移除其父元素(知乎等需要) */\n removeSpecialTagsWithParent?: boolean\n /** 移除 SVG 占位图片 */\n removeSvgImages?: boolean\n\n /** 处理代码块 */\n processCodeBlocks?: boolean\n /** 处理懒加载图片 (data-src → src) */\n processLazyImages?: boolean\n\n /** 移除空元素 (空 p, div, section 等) */\n removeEmptyElements?: boolean\n /** 移除没有有效 src 的 img 标签(src 为空或缺失) */\n removeEmptyImages?: boolean\n /** 移除 data-* 属性 */\n removeDataAttributes?: boolean\n /** 移除 srcset 属性 */\n removeSrcset?: boolean\n /** 移除 sizes 属性 */\n removeSizes?: boolean\n\n /** 将 section 转换为 div */\n convertSectionToDiv?: boolean\n /** 将 section 转换为 p */\n convertSectionToP?: boolean\n\n // 知乎等平台需要的额外处理\n /** 移除段落尾部的 br 标签 */\n removeTrailingBr?: boolean\n /** 解包单一子元素容器 */\n unwrapSingleChildContainers?: boolean\n /** 解包只含单个子 span 的 span(减少过深嵌套) */\n unwrapSingleChildSpans?: boolean\n /** 解包嵌套的 figure 标签 */\n unwrapNestedFigures?: boolean\n /** 展平嵌套的 b/strong 标签(移除内层,保留内容) */\n flattenNestedBold?: boolean\n /** 压缩 HTML(移除标签间空白) */\n compactHtml?: boolean\n\n /** 将表格转换为文本(用 | 分隔列,适用于不支持表格的平台) */\n convertTablesToText?: boolean\n\n /** 保留 <style> 标签(CLI 同步自定义 HTML 时使用) */\n keepStyles?: boolean\n\n // 清理空内容\n /** 移除空行(只含 br 或空白的段落) */\n removeEmptyLines?: boolean\n /** 移除空 div(只含 br 或空白,但保留含图片的) */\n removeEmptyDivs?: boolean\n /** 移除嵌套的空容器 */\n removeNestedEmptyContainers?: boolean\n}\n\n/**\n * 默认预处理配置\n */\nexport const DEFAULT_PREPROCESS_CONFIG: PreprocessConfig = {\n outputFormat: 'html',\n removeIframes: true,\n removeComments: true,\n removeSpecialTags: true,\n removeSvgImages: true,\n processCodeBlocks: true,\n processLazyImages: true,\n removeEmptyElements: true,\n removeDataAttributes: true,\n removeSrcset: true,\n removeSizes: true,\n}\n\n/**\n * 图片上传进度回调\n */\nexport type ImageProgressCallback = (current: number, total: number) => void\n\n/**\n * 发布选项\n */\nexport interface PublishOptions {\n /** 图片上传进度回调 */\n onImageProgress?: ImageProgressCallback\n /** 发布过程事件:扫码、验证码、进度 */\n onEvent?: PublishEventHandler\n}\n\n/**\n * 平台适配器接口\n */\nexport interface PlatformAdapter {\n /** 平台元信息 */\n readonly meta: PlatformMeta\n\n /** 预处理配置 (Content Script 根据此配置预处理内容) */\n readonly preprocessConfig?: Partial<PreprocessConfig>\n\n /** 初始化适配器 */\n init(runtime: RuntimeInterface): Promise<void>\n\n /** 检查认证状态 */\n checkAuth(): Promise<AuthResult>\n\n /** 发布文章 */\n publish(article: Article, options?: PublishOptions): Promise<SyncResult>\n\n /** 查询文章状态 */\n getStatus?(statusRef: PublishStatusRef): Promise<PublishStatusResult>\n\n /** 上传图片 (如果支持) */\n uploadImage?(file: Blob, filename?: string): Promise<string>\n\n /** 获取分类列表 (如果支持) */\n getCategories?(): Promise<Category[]>\n\n /** 获取草稿列表 (如果支持) */\n getDrafts?(): Promise<Draft[]>\n\n /** 更新文章 (如果支持) */\n update?(postId: string, article: Article): Promise<SyncResult>\n\n /** 删除文章 (如果支持) */\n delete?(postId: string): Promise<void>\n}\n\n/**\n * 分类\n */\nexport interface Category {\n id: string\n name: string\n parentId?: string\n}\n\n/**\n * 草稿\n */\nexport interface Draft {\n id: string\n title: string\n updatedAt: number\n}\n\n/**\n * 适配器注册项\n */\nexport interface AdapterRegistryEntry {\n meta: PlatformMeta\n factory: (runtime: RuntimeInterface) => PlatformAdapter\n /** 预处理配置 (Content Script 根据此配置预处理内容) */\n preprocessConfig?: Partial<PreprocessConfig>\n}\n","import type { Article, AuthResult, SyncResult, PlatformMeta } from '../types'\nimport type { RuntimeInterface } from '../runtime/interface'\nimport type { PlatformAdapter } from './types'\n\n/**\n * 适配器基类\n * 提供通用的请求处理和模板解析\n */\nexport abstract class BaseAdapter implements PlatformAdapter {\n abstract readonly meta: PlatformMeta\n protected runtime!: RuntimeInterface\n protected context: Record<string, unknown> = {}\n\n async init(runtime: RuntimeInterface): Promise<void> {\n this.runtime = runtime\n }\n\n abstract checkAuth(): Promise<AuthResult>\n abstract publish(article: Article): Promise<SyncResult>\n\n /**\n * 发送请求\n */\n protected async request<T = unknown>(\n url: string,\n options: RequestInit = {}\n ): Promise<T> {\n const response = await this.runtime.fetch(url, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const contentType = response.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n return response.json()\n }\n\n return response.text() as T\n }\n\n /**\n * 带重试的请求\n */\n protected async requestWithRetry<T = unknown>(\n url: string,\n options: RequestInit = {},\n maxRetries = 3\n ): Promise<T> {\n let lastError: Error | null = null\n\n for (let i = 0; i < maxRetries; i++) {\n try {\n return await this.request<T>(url, options)\n } catch (error) {\n lastError = error as Error\n if (i < maxRetries - 1) {\n await this.delay(1000 * (i + 1))\n }\n }\n }\n\n throw lastError\n }\n\n /**\n * 延迟\n */\n protected delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n }\n\n /**\n * 创建同步结果\n */\n protected createResult(\n success: boolean,\n data?: Partial<SyncResult>\n ): SyncResult {\n return {\n platform: this.meta.id,\n success,\n timestamp: Date.now(),\n ...data,\n }\n }\n}\n","import type { PlatformAdapter, AdapterRegistryEntry, PreprocessConfig } from './types'\nimport { DEFAULT_PREPROCESS_CONFIG } from './types'\nimport type { RuntimeInterface } from '../runtime/interface'\nimport type { PlatformMeta } from '../types'\n\n/**\n * 适配器注册中心\n * 管理所有平台适配器的注册和获取\n */\nclass AdapterRegistry {\n private adapters: Map<string, AdapterRegistryEntry> = new Map()\n private instances: Map<string, PlatformAdapter> = new Map()\n private runtime?: RuntimeInterface\n\n /**\n * 设置运行时\n */\n setRuntime(runtime: RuntimeInterface): void {\n this.runtime = runtime\n // 清空已有实例,等待重新初始化\n this.instances.clear()\n }\n\n /**\n * 注册适配器\n */\n register(entry: AdapterRegistryEntry): void {\n this.adapters.set(entry.meta.id, entry)\n }\n\n /**\n * 批量注册\n */\n registerAll(entries: AdapterRegistryEntry[]): void {\n entries.forEach(entry => this.register(entry))\n }\n\n /**\n * 获取适配器实例\n */\n async get(platformId: string): Promise<PlatformAdapter | null> {\n // 检查缓存\n if (this.instances.has(platformId)) {\n return this.instances.get(platformId)!\n }\n\n // 查找注册项\n const entry = this.adapters.get(platformId)\n if (!entry) {\n return null\n }\n\n if (!this.runtime) {\n throw new Error('Runtime not set. Call setRuntime() first.')\n }\n\n // 创建并初始化实例\n const adapter = entry.factory(this.runtime)\n await adapter.init(this.runtime)\n this.instances.set(platformId, adapter)\n\n return adapter\n }\n\n /**\n * 获取所有平台元信息\n */\n getAllMeta(): PlatformMeta[] {\n return Array.from(this.adapters.values()).map(entry => entry.meta)\n }\n\n /**\n * 检查平台是否已注册\n */\n has(platformId: string): boolean {\n return this.adapters.has(platformId)\n }\n\n /**\n * 获取已注册的平台 ID 列表\n */\n getRegisteredIds(): string[] {\n return Array.from(this.adapters.keys())\n }\n\n /**\n * 清空注册\n */\n clear(): void {\n this.adapters.clear()\n this.instances.clear()\n }\n\n /**\n * 获取平台的预处理配置\n */\n getPreprocessConfig(platformId: string): PreprocessConfig {\n const entry = this.adapters.get(platformId)\n return {\n ...DEFAULT_PREPROCESS_CONFIG,\n ...(entry?.preprocessConfig || {}),\n }\n }\n\n /**\n * 获取多个平台的预处理配置\n */\n getPreprocessConfigs(platformIds: string[]): Record<string, PreprocessConfig> {\n const configs: Record<string, PreprocessConfig> = {}\n for (const id of platformIds) {\n configs[id] = this.getPreprocessConfig(id)\n }\n return configs\n }\n}\n\n/**\n * 全局适配器注册中心实例\n */\nexport const adapterRegistry = new AdapterRegistry()\n\n/**\n * 注册适配器的便捷函数\n */\nexport function registerAdapter(entry: AdapterRegistryEntry): void {\n adapterRegistry.register(entry)\n}\n\n/**\n * 获取适配器的便捷函数\n */\nexport async function getAdapter(platformId: string): Promise<PlatformAdapter | null> {\n return adapterRegistry.get(platformId)\n}\n\n/**\n * 获取平台的预处理配置\n */\nexport function getPreprocessConfig(platformId: string): PreprocessConfig {\n return adapterRegistry.getPreprocessConfig(platformId)\n}\n\n/**\n * 获取多个平台的预处理配置\n */\nexport function getPreprocessConfigs(platformIds: string[]): Record<string, PreprocessConfig> {\n return adapterRegistry.getPreprocessConfigs(platformIds)\n}\n","/**\n * Markdown to Draft.js 转换\n * 用于豆瓣等使用 Draft.js 编辑器的平台\n */\n\n// @ts-ignore - markdown-draft-js 没有类型定义\nimport { markdownToDraft as mdToDraft } from 'markdown-draft-js'\nimport { Remarkable } from 'remarkable'\n\n/**\n * 图片数据接口 - 豆瓣需要完整的图片信息\n */\nexport interface DoubanImageData {\n id: string\n url: string\n thumb: string\n width?: number\n height?: number\n file_name?: string\n file_size?: number\n}\n\n/**\n * 豆瓣图片 Block 解析器 - 处理 ![]() 格式的图片\n */\nconst ImageRegexp = /^!\\[([^\\]]*)]\\s*\\(([^)\"]+)( \"([^)\"]+)\")?\\)/\n\nconst imageBlockPlugin = (remarkable: Remarkable) => {\n // @ts-ignore - remarkable types incomplete\n remarkable.block.ruler.before('paragraph', 'image', (state: any, startLine: number, endLine: number, silent: boolean) => {\n const pos = state.bMarks[startLine] + state.tShift[startLine]\n const max = state.eMarks[startLine]\n\n if (pos >= max) return false\n if (!state.src) return false\n if (state.src[pos] !== '!') return false\n\n const match = ImageRegexp.exec(state.src.slice(pos))\n if (!match) return false\n\n if (!silent) {\n state.tokens.push({\n type: 'image_open',\n src: match[2],\n alt: match[1],\n lines: [startLine, state.line],\n level: state.level\n })\n state.tokens.push({\n type: 'image_close',\n level: state.level\n })\n }\n\n state.line = startLine + 1\n return true\n })\n}\n\n/**\n * 将 Markdown 转换为 Draft.js 格式 (豆瓣专用)\n * @param markdown Markdown 内容\n * @param imageDataMap 图片 URL 到完整数据的映射\n * @returns Draft.js JSON 字符串\n */\nexport function markdownToDraft(markdown: string, imageDataMap: Map<string, DoubanImageData> = new Map()): string {\n // 保证图片换行\n const processedMarkdown = markdown.split('\\n').map(line => {\n const imageBlocks = line.split('![]')\n return imageBlocks.length > 1 ? imageBlocks.join('\\n![]') : line\n }).join('\\n')\n\n let keyCounter = 0\n const generateUniqueKey = () => keyCounter++\n\n const draftState = mdToDraft(processedMarkdown, {\n remarkablePlugins: [imageBlockPlugin],\n blockTypes: {\n image_open: function (item: any) {\n const key = generateUniqueKey()\n const blockEntities: Record<number, any> = {}\n\n // 解析 ?# 格式获取原始 URL 和 ID\n const sourcePair = item.src ? item.src.split('?#') : ['', '']\n const rawSrc = sourcePair[0]\n const sourceId = sourcePair[1] || ''\n\n // 从 imageDataMap 获取完整图片数据\n const imgData = imageDataMap.get(item.src) || imageDataMap.get(rawSrc)\n\n const imageTemplate = imgData ? {\n id: imgData.id,\n src: imgData.url,\n thumb: imgData.thumb,\n url: imgData.url,\n width: imgData.width,\n height: imgData.height,\n file_name: imgData.file_name,\n file_size: imgData.file_size,\n } : {\n id: sourceId,\n src: rawSrc,\n thumb: rawSrc,\n url: rawSrc,\n }\n\n blockEntities[key] = {\n type: 'IMAGE',\n mutability: 'IMMUTABLE',\n data: imageTemplate,\n }\n\n return {\n type: 'atomic',\n blockEntities: blockEntities,\n inlineStyleRanges: [],\n entityRanges: [{ offset: 0, length: 1, key: key }],\n text: ' ',\n }\n }\n },\n blockEntities: {\n image: function (item: any) {\n const sourcePair = item.src ? item.src.split('?#') : ['', '']\n const rawSrc = sourcePair[0]\n const sourceId = sourcePair[1] || ''\n\n // 从 imageDataMap 获取完整图片数据\n const imgData = imageDataMap.get(item.src) || imageDataMap.get(rawSrc)\n\n if (imgData) {\n return {\n type: 'IMAGE',\n mutability: 'IMMUTABLE',\n data: {\n id: imgData.id,\n src: imgData.url,\n thumb: imgData.thumb,\n url: imgData.url,\n width: imgData.width,\n height: imgData.height,\n }\n }\n }\n\n return {\n type: 'IMAGE',\n mutability: 'IMMUTABLE',\n data: {\n id: sourceId,\n src: rawSrc,\n thumb: rawSrc,\n url: rawSrc,\n }\n }\n }\n }\n })\n\n // 将 block.blockEntities 合并到顶层 entityMap (参考 mtd.js)\n if (draftState.blocks) {\n for (const block of draftState.blocks) {\n if (block.blockEntities) {\n Object.assign(draftState.entityMap, block.blockEntities)\n delete block.blockEntities\n }\n }\n }\n\n return JSON.stringify(draftState)\n}\n","/**\n * 豆瓣适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { DoubanImageData } from '../../lib'\nimport type { PublishOptions } from '../types'\nimport { markdownToDraft } from '../../lib'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Douban')\n\ninterface DoubanFormData {\n note_id: string\n ck: string\n}\n\ninterface DoubanPostParams {\n siteCookie: {\n value: string\n }\n}\n\nexport class DoubanAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'douban',\n name: '豆瓣',\n icon: 'https://www.douban.com/favicon.ico',\n homepage: 'https://www.douban.com/note/create',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 豆瓣使用 Markdown 格式 (转换为 Draft.js) */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n private username: string = ''\n private avatar: string = ''\n private formData: DoubanFormData | null = null\n private postParams: DoubanPostParams | null = null\n\n /** 豆瓣 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://www.douban.com/*',\n headers: {\n 'Origin': 'https://www.douban.com',\n 'Referer': 'https://www.douban.com',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch(\n 'https://www.douban.com/note/create',\n {\n method: 'GET',\n credentials: 'include',\n }\n )\n\n const html = await response.text()\n\n // 解析页面中的 JavaScript 变量\n const userNameMatch = html.match(/_USER_NAME\\s*=\\s*['\"]([^'\"]+)['\"]/)\n const userAvatarMatch = html.match(/_USER_AVATAR\\s*=\\s*['\"]([^'\"]+)['\"]/)\n const noteIdMatch = html.match(/name=\"note_id\"\\s+value=\"(\\d+)\"/)\n const ckMatch = html.match(/name=\"ck\"\\s+value=\"([^\"]+)\"/)\n\n // 解析 _POST_PARAMS\n const postParamsMatch = html.match(/_POST_PARAMS\\s*=\\s*(\\{[\\s\\S]*?\\});/)\n\n if (!userNameMatch || !noteIdMatch || !ckMatch) {\n return { isAuthenticated: false }\n }\n\n this.username = userNameMatch[1]\n this.avatar = userAvatarMatch ? userAvatarMatch[1] : ''\n this.formData = {\n note_id: noteIdMatch[1],\n ck: ckMatch[1],\n }\n\n // 解析 _POST_PARAMS 获取 upload_auth_token\n if (postParamsMatch) {\n try {\n // 简化解析,只提取 siteCookie.value\n const siteCookieMatch = postParamsMatch[1].match(/siteCookie[^}]*value\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (siteCookieMatch) {\n this.postParams = {\n siteCookie: { value: siteCookieMatch[1] }\n }\n }\n } catch (e) {\n logger.warn('Failed to parse _POST_PARAMS:', e)\n }\n }\n\n logger.debug('Auth info:', {\n username: this.username,\n noteId: this.formData.note_id,\n hasPostParams: !!this.postParams,\n })\n\n return {\n isAuthenticated: true,\n userId: this.username,\n username: this.username,\n avatar: this.avatar,\n }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', error)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n // 1. 确保已登录\n if (!this.formData) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('请先登录豆瓣')\n }\n }\n\n // Use pre-processed markdown content directly\n let content = article.markdown || ''\n\n // Process images - collect full image data\n const imageDataMap = new Map<string, DoubanImageData>()\n content = await this.processImages(\n content,\n async (src) => {\n const result = await this.uploadImageWithFullData(src)\n // 保存完整图片数据,用 newUrl 作为 key\n imageDataMap.set(result.url, result.imageData)\n return result\n },\n {\n skipPatterns: ['doubanio.com', 'douban.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n // Markdown to Draft.js format (pass in image data)\n const draftContent = markdownToDraft(content, imageDataMap)\n\n // 6. 保存草稿\n const response = await this.runtime.fetch(\n 'https://www.douban.com/j/note/autosave',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n is_rich: '1',\n note_id: this.formData!.note_id,\n note_title: article.title,\n note_text: draftContent,\n introduction: '',\n note_privacy: 'P',\n cannot_reply: '',\n author_tags: '',\n accept_donation: '',\n donation_notice: '',\n is_original: '',\n ck: this.formData!.ck,\n }),\n }\n )\n\n const res = await response.json() as { url?: string; r?: number }\n logger.debug('Save response:', res)\n\n // 豆瓣草稿只能在 /note/create 页面查看\n const draftUrl = 'https://www.douban.com/note/create'\n\n return this.createResult(true, {\n postId: this.formData!.note_id,\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n /**\n * 上传图片并返回完整数据\n */\n private async uploadImageWithFullData(src: string): Promise<ImageUploadResult & { imageData: DoubanImageData }> {\n if (!this.formData || !this.postParams) {\n throw new Error('未获取上传凭证')\n }\n\n // 1. 下载图片\n const imageResponse = await fetch(src)\n if (!imageResponse.ok) {\n throw new Error('图片下载失败: ' + src)\n }\n const imageBlob = await imageResponse.blob()\n\n // 2. 上传到豆瓣\n const formData = new FormData()\n formData.append('note_id', this.formData.note_id)\n formData.append('image_file', imageBlob, 'image.jpg')\n formData.append('ck', this.formData.ck)\n formData.append('upload_auth_token', this.postParams.siteCookie.value)\n\n const uploadResponse = await this.runtime.fetch(\n 'https://www.douban.com/j/note/add_photo',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await uploadResponse.json() as {\n photo?: {\n id: string\n url: string\n thumb: string\n width: number\n height: number\n file_name: string\n file_size: number\n }\n }\n\n logger.debug('Image upload response:', res)\n\n if (!res.photo?.url) {\n throw new Error('图片上传失败')\n }\n\n const photo = res.photo\n\n // 返回带完整图片数据\n return {\n url: photo.url,\n imageData: {\n id: photo.id,\n url: photo.url,\n thumb: photo.thumb,\n width: photo.width,\n height: photo.height,\n file_name: photo.file_name,\n file_size: photo.file_size,\n }\n }\n }\n}\n","/**\n * 雪球适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport { Remarkable } from 'remarkable'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Xueqiu')\n\ninterface XueqiuUser {\n id: string\n screen_name: string\n photo_domain: string\n profile_image_url: string\n}\n\nexport class XueqiuAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'xueqiu',\n name: '雪球',\n icon: 'https://xqdoc.imedao.com/17aebcfb84a145d33fc18679.ico',\n homepage: 'https://mp.xueqiu.com/writeV2',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 雪球使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n // doPreFilter + processDocCode (旧版)\n removeSpecialTags: true,\n removeSpecialTagsWithParent: true,\n processCodeBlocks: true,\n }\n\n private currentUser: XueqiuUser | null = null\n\n /** 雪球 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://mp.xueqiu.com/xq/*',\n headers: {\n 'Origin': 'https://mp.xueqiu.com',\n 'Referer': 'https://mp.xueqiu.com/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch(\n 'https://mp.xueqiu.com/writeV2',\n {\n method: 'GET',\n credentials: 'include',\n }\n )\n\n const html = await response.text()\n\n // 解析 window.UOM_CURRENTUSER - 新版格式\n const userMatch = html.match(/window\\.UOM_CURRENTUSER\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*<\\/script>/)\n if (!userMatch) {\n return { isAuthenticated: false }\n }\n\n try {\n const state = JSON.parse(userMatch[1])\n const { currentUser } = state\n\n if (!currentUser?.id) {\n return { isAuthenticated: false }\n }\n\n this.currentUser = currentUser\n\n const avatar = currentUser.photo_domain && currentUser.profile_image_url\n ? `https:${currentUser.photo_domain}${currentUser.profile_image_url.split(',')[0]}`\n : ''\n\n return {\n isAuthenticated: true,\n userId: String(currentUser.id),\n username: currentUser.screen_name,\n avatar,\n }\n } catch (e) {\n logger.error(' Failed to parse user data:', e)\n return { isAuthenticated: false }\n }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', error)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n // 1. 确保已登录\n if (!this.currentUser) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('请先登录雪球')\n }\n }\n\n // Use pre-processed markdown content directly\n let markdown = article.markdown || ''\n\n // Process images in markdown\n markdown = await this.processImages(\n markdown,\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: ['xueqiu.com', 'imedao.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n // Convert Markdown to simplified HTML (Xueqiu format)\n const md = new Remarkable({\n html: true,\n breaks: true,\n })\n\n // 自定义渲染规则适配雪球格式\n // 所有标题都转为 h4\n md.renderer.rules.heading_open = () => '<h4>'\n md.renderer.rules.heading_close = () => '</h4>'\n\n // strong -> b\n md.renderer.rules.strong_open = () => '<b>'\n md.renderer.rules.strong_close = () => '</b>'\n\n // em -> i\n md.renderer.rules.em_open = () => '<i>'\n md.renderer.rules.em_close = () => '</i>'\n\n // 列表 - 移除列表包装,列表项内的 p 标签由 remarkable 自动处理\n md.renderer.rules.bullet_list_open = () => ''\n md.renderer.rules.bullet_list_close = () => ''\n md.renderer.rules.ordered_list_open = () => ''\n md.renderer.rules.ordered_list_close = () => ''\n md.renderer.rules.list_item_open = () => ''\n md.renderer.rules.list_item_close = () => ''\n\n // 移除 hr\n md.renderer.rules.hr = () => ''\n\n // 图片添加 class\n md.renderer.rules.image = (tokens: any[], idx: number) => {\n const src = tokens[idx].src || ''\n const alt = tokens[idx].alt || ''\n return `<img src=\"${src}\" alt=\"${alt}\" class=\"ke_img\">`\n }\n\n let rendered = md.render(markdown)\n\n // Clean up: remove empty p tags and excessive newlines\n rendered = rendered\n .replace(/<p>\\s*<\\/p>/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n\n const content = rendered\n\n // 4. 保存草稿\n const formData = new URLSearchParams({\n text: content,\n title: article.title,\n cover_pic: '',\n flags: 'false',\n original_event: '',\n status_id: '',\n legal_user_visible: 'false',\n is_private: 'false',\n })\n\n const response = await this.runtime.fetch(\n 'https://mp.xueqiu.com/xq/statuses/draft/save.json',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: formData,\n }\n )\n\n const res = await response.json() as {\n id?: string | number\n error_description?: string\n }\n\n logger.debug(' Save response:', res)\n\n if (!res.id) {\n throw new Error(res.error_description || '保存失败')\n }\n\n const postId = res.id\n const draftUrl = `https://mp.xueqiu.com/write/draft/${postId}`\n\n return this.createResult(true, {\n postId: String(postId),\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n /**\n * 通过 URL 上传图片\n */\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n // 1. 下载图片\n const imageResponse = await fetch(src)\n if (!imageResponse.ok) {\n throw new Error('图片下载失败: ' + src)\n }\n const imageBlob = await imageResponse.blob()\n\n // 2. 上传到雪球\n const formData = new FormData()\n formData.append('file', imageBlob, 'image.jpg')\n\n const uploadResponse = await this.runtime.fetch(\n 'https://mp.xueqiu.com/xq/photo/upload.json',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await uploadResponse.json() as {\n url?: string\n filename?: string\n }\n\n logger.debug(' Image upload response:', res)\n\n if (!res.url || !res.filename) {\n throw new Error('图片上传失败')\n }\n\n // 雪球返回的 url 是 //开头,需要加 https:\n const fullUrl = res.url.startsWith('//') ? `https:${res.url}/${res.filename}` : `${res.url}/${res.filename}`\n\n return {\n url: fullUrl,\n }\n }\n}\n","/**\n * 人人都是产品经理 (woshipm.com) 适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Woshipm')\n\nexport class WoshipmAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'woshipm',\n name: '人人都是产品经理',\n icon: 'https://www.woshipm.com/favicon.ico',\n homepage: 'https://www.woshipm.com',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 人人都是产品经理使用 HTML 格式 */\n readonly preprocessConfig = {\n outputFormat: 'html' as const,\n removeEmptyLines: true,\n }\n\n private jltoken: string = ''\n\n /** 人人都是产品经理 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://woshipm.com/wp-admin/admin-ajax.php*',\n headers: { 'X-Requested-With': 'XMLHttpRequest' },\n resourceTypes: ['xmlhttprequest'],\n },\n {\n urlFilter: '*://woshipm.com/api2/*',\n headers: { 'X-Requested-With': 'XMLHttpRequest' },\n resourceTypes: ['xmlhttprequest'],\n },\n {\n urlFilter: '*://woshipm.com/tensorflow/upyun/upload*',\n headers: { 'X-Requested-With': 'XMLHttpRequest' },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n async checkAuth(): Promise<AuthResult> {\n try {\n // 1. 先获取用户页面以获取 uid\n const pageResponse = await this.runtime.fetch('https://www.woshipm.com/writing', {\n method: 'GET',\n credentials: 'include',\n })\n\n const pageText = await pageResponse.text()\n\n // 从页面提取 jltoken: \"jltoken\":\"xxx\"\n const jltokenMatch = pageText.match(/\"jltoken\"\\s*:\\s*\"([^\"]+)\"/)\n if (jltokenMatch) {\n this.jltoken = jltokenMatch[1]\n logger.debug('Found jltoken')\n }\n\n // 从页面提取 uid: var userSettings = {\"url\":\"\\/\",\"uid\":\"1585\",...}\n const uidMatch = pageText.match(/var\\s+userSettings\\s*=\\s*\\{[^}]*\"uid\"\\s*:\\s*\"(\\d+)\"/)\n if (!uidMatch) {\n return { isAuthenticated: false }\n }\n\n const uid = uidMatch[1]\n\n // 2. 调用 profile API 验证登录状态\n const response = await this.runtime.fetch(\n `https://www.woshipm.com/api2/user/profile?uid=${uid}`,\n {\n method: 'GET',\n credentials: 'include',\n headers: {\n 'X-Requested-With': 'XMLHttpRequest',\n },\n }\n )\n\n const data = await response.json() as {\n CODE?: number\n RESULT?: {\n userInfoVo?: {\n uid?: number\n nickName?: string\n avartar?: string // API typo: avartar instead of avatar\n }\n }\n }\n\n if (data.CODE === 200 && data.RESULT?.userInfoVo?.uid) {\n return {\n isAuthenticated: true,\n userId: String(data.RESULT.userInfoVo.uid),\n username: data.RESULT.userInfoVo.nickName,\n avatar: data.RESULT.userInfoVo.avartar,\n }\n }\n\n return { isAuthenticated: false }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', error)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n // 1. 使用预处理好的 HTML(Content Script 已处理代码块、图片、特殊标签等)\n // 人人都是产品经理使用 HTML 格式\n let content = article.html || ''\n\n // 2. 处理图片\n content = await this.processImages(\n content,\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: ['woshipm.com', 'image.woshipm.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n // 4. 创建草稿\n const createResponse = await this.runtime.fetch(\n 'https://www.woshipm.com/wp-admin/admin-ajax.php',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'X-Requested-With': 'XMLHttpRequest',\n },\n body: new URLSearchParams({\n action: 'add_draft',\n post_title: article.title,\n post_content: content,\n }),\n }\n )\n\n // 检查响应状态和内容\n const responseText = await createResponse.text()\n logger.debug('Create draft response:', createResponse.status, responseText.substring(0, 300))\n\n if (!createResponse.ok) {\n throw new Error(`创建草稿失败: ${createResponse.status} - ${responseText}`)\n }\n\n let createData: { post_id?: string | number; url?: string; success?: boolean; error?: string }\n try {\n createData = JSON.parse(responseText)\n } catch {\n throw new Error(`创建草稿失败: 响应不是有效 JSON - ${responseText.substring(0, 100)}`)\n }\n\n if (!createData.post_id) {\n throw new Error(createData.error || '创建草稿失败: 无效响应')\n }\n\n const draftId = String(createData.post_id)\n const draftUrl = createData.url || `https://www.woshipm.com/writing?pid=${draftId}`\n\n logger.debug('Draft created:', draftId)\n\n return this.createResult(true, {\n postId: draftId,\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n /**\n * 通过 Blob 上传图片(覆盖基类方法)\n */\n async uploadImage(file: Blob, filename?: string): Promise<string> {\n return this.uploadImageBinaryInternal(file, filename || 'image.png')\n }\n\n /**\n * 通过 URL 上传图片\n */\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n try {\n // 1. 下载图片(使用 runtime.fetch 以支持跨域)\n const imageResponse = await this.runtime.fetch(src, {\n credentials: 'omit',\n })\n if (!imageResponse.ok) {\n throw new Error(`Failed to fetch image: ${imageResponse.status}`)\n }\n\n const blob = await imageResponse.blob()\n\n // 2. 上传到 woshipm\n const url = await this.uploadImageBinaryInternal(blob, this.getFilenameFromUrl(src))\n return { url }\n } catch (error) {\n logger.warn('Failed to upload image by URL:', src, error)\n return { url: src } // 失败时返回原 URL\n }\n }\n\n /**\n * 上传图片 (二进制方式) - 内部使用\n */\n private async uploadImageBinaryInternal(file: Blob, filename: string): Promise<string> {\n const formData = new FormData()\n formData.append('action', 'wpuf_insert_image')\n formData.append('name', filename)\n formData.append('files', file, filename)\n\n const headers: Record<string, string> = {\n 'Origin': 'https://www.woshipm.com',\n 'Referer': 'https://www.woshipm.com/writing',\n }\n if (this.jltoken) {\n headers['jlstar'] = `Bearer ${this.jltoken}`\n }\n\n const response = await this.runtime.fetch('https://www.woshipm.com/tensorflow/upyun/upload', {\n method: 'POST',\n credentials: 'include',\n headers,\n body: formData,\n })\n\n const data = await response.json() as {\n data?: Array<{ url?: string }>\n error?: string\n }\n\n if (data.data && data.data.length > 0 && data.data[0].url) {\n logger.debug('Uploaded image:', filename, '->', data.data[0].url)\n return data.data[0].url\n }\n\n throw new Error(data.error || 'Failed to upload image')\n }\n\n /**\n * 从 URL 提取文件名\n */\n private getFilenameFromUrl(url: string): string {\n try {\n const pathname = new URL(url).pathname\n const filename = pathname.split('/').pop() || 'image.png'\n return filename\n } catch {\n return 'image.png'\n }\n }\n}\n","/**\n * 语雀适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Yuque')\n\ninterface YuqueUserInfo {\n id: number\n name: string\n avatar_url: string\n}\n\ninterface YuqueBook {\n target_id: number\n user: YuqueUserInfo\n}\n\nexport class YuqueAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'yuque',\n name: '语雀',\n icon: 'https://gw.alipayobjects.com/zos/rmsportal/UTjFYEzMSYVwzxIGVhMu.png',\n homepage: 'https://www.yuque.com/dashboard',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 语雀使用 Markdown 格式 (转换为 lake) */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n // doPreFilter + processDocCode (旧版)\n removeSpecialTags: true,\n removeSpecialTagsWithParent: true,\n processCodeBlocks: true,\n }\n\n private userInfo: YuqueUserInfo | null = null\n private bookId: number | null = null\n private csrfToken: string = ''\n private currentPostId: number | null = null\n\n /** 语雀 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://www.yuque.com/api/*',\n headers: {\n 'Origin': 'https://www.yuque.com',\n 'Referer': 'https://www.yuque.com/dashboard',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n private async getCsrfToken(): Promise<string> {\n if (this.runtime.getCookie) {\n const value = await this.runtime.getCookie('.yuque.com', 'yuque_ctoken')\n if (!value) {\n throw new Error('请先登录语雀')\n }\n return value\n }\n throw new Error('请先登录语雀')\n }\n\n async checkAuth(): Promise<AuthResult> {\n try {\n this.csrfToken = await this.getCsrfToken()\n\n const response = await this.runtime.fetch(\n 'https://www.yuque.com/api/mine/common_used',\n {\n method: 'GET',\n credentials: 'include',\n headers: {\n 'x-csrf-token': this.csrfToken,\n },\n }\n )\n\n const res = await response.json() as {\n data?: {\n books?: YuqueBook[]\n }\n }\n\n logger.debug('checkAuth response:', res)\n\n if (res.data?.books && res.data.books.length > 0) {\n const firstBook = res.data.books[0]\n this.userInfo = firstBook.user\n this.bookId = firstBook.target_id\n\n return {\n isAuthenticated: true,\n userId: String(firstBook.user.id),\n username: firstBook.user.name,\n avatar: firstBook.user.avatar_url,\n }\n }\n\n return { isAuthenticated: false }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', (error as Error).message)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n if (!this.userInfo || !this.bookId) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('请先登录语雀')\n }\n }\n\n const createResponse = await this.runtime.fetch(\n 'https://www.yuque.com/api/docs',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n 'x-csrf-token': this.csrfToken,\n },\n body: JSON.stringify({\n title: article.title,\n type: 'Doc',\n format: 'lake',\n book_id: this.bookId,\n status: 0,\n }),\n }\n )\n\n const createRes = await createResponse.json() as {\n data?: { id: number }\n message?: string\n }\n\n logger.debug('Create doc response:', createRes)\n\n if (!createRes.data?.id) {\n throw new Error(createRes.message || '创建文档失败')\n }\n\n const postId = createRes.data.id\n this.currentPostId = postId\n\n // Use pre-processed markdown content directly\n let markdown = article.markdown || ''\n\n // Process images in markdown\n markdown = await this.processImages(\n markdown,\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: ['yuque.com', 'cdn.nlark.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n const convertResponse = await this.runtime.fetch(\n 'https://www.yuque.com/api/docs/convert',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n 'x-csrf-token': this.csrfToken,\n },\n body: JSON.stringify({\n from: 'markdown',\n to: 'lake',\n content: markdown,\n }),\n }\n )\n\n const convertRes = await convertResponse.json() as {\n data?: { content: string }\n }\n\n if (!convertRes.data?.content) {\n throw new Error('内容转换失败')\n }\n\n const lakeContent = convertRes.data.content\n\n const saveResponse = await this.runtime.fetch(\n `https://www.yuque.com/api/docs/${postId}/content`,\n {\n method: 'PUT',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n 'x-csrf-token': this.csrfToken,\n },\n body: JSON.stringify({\n format: 'lake',\n body_asl: lakeContent,\n body: `<div class=\"lake-content\" typography=\"traditional\">${lakeContent}</div>`,\n body_html: `<div class=\"lake-content\" typography=\"traditional\">${lakeContent}</div>`,\n draft_version: 0,\n sync_dynamic_data: false,\n save_type: 'auto',\n edit_type: 'Lake',\n }),\n }\n )\n\n const saveRes = await saveResponse.json()\n logger.debug('Save response:', saveRes)\n\n const draftUrl = `https://www.yuque.com/go/doc/${postId}/edit`\n\n return this.createResult(true, {\n postId: String(postId),\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n if (!this.currentPostId) {\n throw new Error('文档 ID 未设置')\n }\n\n const imageResponse = await fetch(src)\n if (!imageResponse.ok) {\n throw new Error('图片下载失败: ' + src)\n }\n const imageBlob = await imageResponse.blob()\n\n const formData = new FormData()\n formData.append('file', imageBlob, 'image.jpg')\n\n const uploadUrl = `https://www.yuque.com/api/upload/attach?attachable_type=Doc&attachable_id=${this.currentPostId}&type=image`\n const uploadResponse = await this.runtime.fetch(uploadUrl, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'x-csrf-token': this.csrfToken,\n },\n body: formData,\n })\n\n const res = await uploadResponse.json() as {\n data?: {\n attachment_id: string\n url: string\n }\n }\n\n logger.debug('Image upload response:', res)\n\n if (!res.data?.url) {\n throw new Error('图片上传失败')\n }\n\n return {\n url: res.data.url,\n }\n }\n}\n","/**\n * 慕课网手记适配器\n * https://www.imooc.com\n */\nimport { CodeAdapter, ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nexport class ImoocAdapter extends CodeAdapter {\n meta: PlatformMeta = {\n id: 'imooc',\n name: '慕课手记',\n icon: 'https://www.imooc.com/favicon.ico',\n homepage: 'https://www.imooc.com/article',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 慕课网使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n /** 慕课网 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://www.imooc.com/article/*',\n headers: {\n Origin: 'https://www.imooc.com',\n Referer: 'https://www.imooc.com/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n /**\n * 检查登录状态\n */\n async checkAuth(): Promise<AuthResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n const response = await this.runtime.fetch('https://www.imooc.com/u/card', {\n credentials: 'include',\n })\n let text = await response.text()\n\n // 解析 JSONP 响应\n text = text.replace('jsonpcallback(', '').replace('})', '}')\n const result = JSON.parse(text)\n\n if (result.result !== 0) {\n return { isAuthenticated: false, error: result.msg || '未登录' }\n }\n\n return {\n isAuthenticated: true,\n userId: result.data.uid,\n username: result.data.nickname,\n avatar: result.data.img,\n }\n }).catch((error) => ({ isAuthenticated: false, error: (error as Error).message }))\n }\n\n /**\n * 上传图片\n */\n async uploadImageByUrl(url: string): Promise<ImageUploadResult> {\n // 下载图片\n const imageResponse = await this.runtime.fetch(url)\n const blob = await imageResponse.blob()\n\n // 构建 FormData\n const formData = new FormData()\n const filename = `${Date.now()}.jpg`\n const file = new File([blob], filename, { type: blob.type || 'image/jpeg' })\n\n formData.append('photo', file, filename)\n formData.append('type', file.type)\n formData.append('id', 'WU_FILE_0')\n formData.append('name', filename)\n formData.append('lastModifiedDate', new Date().toString())\n formData.append('size', String(file.size))\n\n const response = await this.runtime.fetch(\n 'https://www.imooc.com/article/ajaxuploadimg',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await response.json()\n\n if (res.result !== 0) {\n throw new Error(res.msg || '图片上传失败')\n }\n\n // 处理协议相对 URL\n let imgUrl = res.data.imgpath\n if (imgUrl.startsWith('//')) {\n imgUrl = 'https:' + imgUrl\n }\n\n return { url: imgUrl }\n }\n\n /**\n * 发布文章\n */\n async publish(article: Article): Promise<SyncResult> {\n const now = Date.now()\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n // 优先使用 markdown,处理图片\n let content = article.markdown || article.html || ''\n content = await this.processImages(content, (src) => this.uploadImageByUrl(src))\n\n const response = await this.runtime.fetch('https://www.imooc.com/article/savedraft', {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n editor: '0',\n draft_id: '0',\n title: article.title,\n content: content,\n }),\n })\n\n const res = await response.json()\n\n if (!res.data) {\n throw new Error('发布失败')\n }\n\n return {\n platform: this.meta.id,\n success: true,\n postId: res.data,\n postUrl: `https://www.imooc.com/article/draft/id/${res.data}`,\n timestamp: now,\n }\n }).catch((error) => ({\n platform: this.meta.id,\n success: false,\n error: (error as Error).message,\n timestamp: now,\n }))\n }\n}\n","/**\n * 开源中国适配器\n * https://my.oschina.net\n */\nimport { CodeAdapter, ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nexport class OschinaAdapter extends CodeAdapter {\n meta: PlatformMeta = {\n id: 'oschina',\n name: '开源中国',\n icon: 'https://www.oschina.net/favicon.ico',\n homepage: 'https://my.oschina.net',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 开源中国使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n private userId: string | null = null\n\n /** 开源中国 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://apiv1.oschina.net/oschinapi/*',\n headers: {\n Origin: 'https://my.oschina.net',\n Referer: 'https://my.oschina.net/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n /**\n * 检查登录状态\n */\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch('https://apiv1.oschina.net/oschinapi/user/myDetails', {\n credentials: 'include',\n })\n const data = await response.json() as {\n success: boolean\n result?: {\n userId: number\n userVo?: {\n name: string\n portraitUrl: string\n }\n }\n }\n\n if (!data.success || !data.result?.userId) {\n return { isAuthenticated: false, error: '未登录' }\n }\n\n this.userId = String(data.result.userId)\n\n return {\n isAuthenticated: true,\n userId: this.userId,\n username: data.result.userVo?.name || this.userId,\n avatar: data.result.userVo?.portraitUrl,\n }\n } catch (error) {\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n /**\n * 上传图片\n */\n async uploadImageByUrl(url: string): Promise<ImageUploadResult> {\n if (!this.userId) {\n await this.checkAuth()\n }\n\n // 下载图片\n const imageResponse = await this.runtime.fetch(url)\n const blob = await imageResponse.blob()\n const filename = this.getFilenameFromUrl(url) || 'image'\n\n // 构建 FormData\n const formData = new FormData()\n formData.append('file', blob, filename)\n\n const response = await this.runtime.fetch(\n 'https://apiv1.oschina.net/oschinapi/ai/creation/project/uploadDetail',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await response.json() as {\n success?: boolean\n result?: string\n message?: string\n }\n\n if (!res.success || !res.result) {\n throw new Error(res.message || '图片上传失败')\n }\n\n return { url: res.result }\n }\n\n private getFilenameFromUrl(url: string): string | null {\n try {\n const pathname = new URL(url).pathname\n const name = pathname.split('/').pop()\n return name && name.trim() ? name : null\n } catch {\n return null\n }\n }\n\n /**\n * 发布文章\n */\n async publish(article: Article): Promise<SyncResult> {\n const now = Date.now()\n\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n // 确保已获取用户 ID\n if (!this.userId) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('未登录')\n }\n }\n\n const rawMarkdown = article.markdown || ''\n const rawHtml = article.html || ''\n const useMarkdown = rawMarkdown.trim().length > 0\n\n let content = useMarkdown ? rawMarkdown : rawHtml\n content = await this.processImages(content, (src) => this.uploadImageByUrl(src))\n\n const response = await this.runtime.fetch(\n 'https://apiv1.oschina.net/oschinapi/api/draft/save_draft',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n title: article.title,\n user: Number(this.userId),\n content,\n contentType: useMarkdown ? 1 : 2, // 1=markdown, 2=html\n catalog: 0,\n originUrl: '',\n privacy: true,\n disableComment: false,\n }),\n }\n )\n\n const res = await response.json() as {\n success?: boolean\n message?: string\n result?: { id?: number }\n }\n\n if (!res.success || !res.result?.id) {\n throw new Error(res.message || '发布失败')\n }\n\n const draftId = String(res.result.id)\n\n return {\n platform: this.meta.id,\n success: true,\n postId: draftId,\n postUrl: `https://my.oschina.net/u/${this.userId}/blog/write/draft/${draftId}`,\n timestamp: now,\n }\n }).catch((error) => ({\n platform: this.meta.id,\n success: false,\n error: (error as Error).message,\n timestamp: now,\n }))\n }\n}\n","/**\n * 思否 (Segmentfault) 适配器\n * https://segmentfault.com\n */\nimport { CodeAdapter, ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nexport class SegmentfaultAdapter extends CodeAdapter {\n meta: PlatformMeta = {\n id: 'segmentfault',\n name: '思否',\n icon: 'https://imgcache.iyiou.com/Company/2016-05-11/cf-segmentfault.jpg',\n homepage: 'https://segmentfault.com/user/draft',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 思否使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n private sessionToken: string | null = null\n\n /** 思否 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://segmentfault.com/gateway/*',\n headers: {\n Origin: 'https://segmentfault.com',\n Referer: 'https://segmentfault.com/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n /**\n * 检查登录状态\n */\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch('https://segmentfault.com/user/settings', {\n credentials: 'include',\n })\n const html = await response.text()\n\n // 匹配用户链接 href=\"/u/username\"\n const userLinkMatch = html.match(/href=\"\\/u\\/([^\"]+)\"/)\n if (!userLinkMatch) {\n return { isAuthenticated: false, error: '未登录' }\n }\n\n const uid = userLinkMatch[1]\n\n // 匹配头像 URL (avatar-static.segmentfault.com)\n const avatarMatch = html.match(/src=\"(https:\\/\\/avatar-static\\.segmentfault\\.com\\/[^\"]+)\"/)\n const avatar = avatarMatch ? avatarMatch[1] : undefined\n\n return {\n isAuthenticated: true,\n userId: uid,\n username: uid,\n avatar: avatar,\n }\n } catch (error) {\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n /**\n * 获取 session token\n */\n private async getSessionToken(): Promise<string> {\n const response = await this.runtime.fetch('https://segmentfault.com/write', {\n credentials: 'include',\n })\n const html = await response.text()\n\n // 新版 token 格式: serverData\":{\"Token\":\"xxx\"\n const tokenMatch = html.match(/serverData\":\\s*\\{\\s*\"Token\"\\s*:\\s*\"([^\"]+)\"/)\n if (tokenMatch) {\n return tokenMatch[1]\n }\n\n // 兼容旧版格式\n const markStr = 'window.g_initialProps = '\n const authIndex = html.indexOf(markStr)\n if (authIndex === -1) {\n throw new Error('获取 session token 失败')\n }\n\n const endIndex = html.indexOf(';\\n\\t</script>', authIndex)\n if (endIndex === -1) {\n throw new Error('解析 session token 失败')\n }\n\n const configStr = html.substring(authIndex + markStr.length, endIndex)\n\n try {\n const config = JSON.parse(configStr)\n const token = config?.global?.sessionInfo?.key\n if (!token) {\n throw new Error('session token 为空')\n }\n return token\n } catch (e) {\n throw new Error('解析 session token 失败: ' + (e as Error).message)\n }\n }\n\n /**\n * 上传图片\n */\n async uploadImageByUrl(url: string): Promise<ImageUploadResult> {\n if (!this.sessionToken) {\n throw new Error('未获取 token')\n }\n\n // 下载图片\n const imageResponse = await this.runtime.fetch(url)\n const blob = await imageResponse.blob()\n\n // 构建 FormData\n const formData = new FormData()\n formData.append('image', blob)\n\n const response = await this.runtime.fetch(\n 'https://segmentfault.com/gateway/image',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n token: this.sessionToken,\n },\n body: formData,\n }\n )\n\n // 处理异常响应\n const text = await response.text()\n if (text === 'Unauthorized' || text.includes('禁言') || text.includes('锁定')) {\n throw new Error(text === 'Unauthorized' ? '未授权' : text)\n }\n\n let res\n try {\n res = JSON.parse(text)\n } catch {\n throw new Error('图片上传失败: ' + text)\n }\n\n // 新版返回格式: { url: \"/img/xxx\", result: \"https://...\" }\n // 旧版返回格式: [0, url, id] 或 [1, error_message]\n const imageUrl = res.result || (Array.isArray(res) ? (res[0] === 1 ? null : res[1] || `https://image-static.segmentfault.com/${res[2]}`) : null)\n if (!imageUrl) {\n throw new Error(Array.isArray(res) ? (res[1] || '图片上传失败') : '图片上传失败')\n }\n return { url: imageUrl }\n }\n\n /**\n * 发布文章\n */\n async publish(article: Article): Promise<SyncResult> {\n const now = Date.now()\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n // 获取 session token\n this.sessionToken = await this.getSessionToken()\n\n // 优先使用 markdown,处理图片\n let content = article.markdown || article.html || ''\n content = await this.processImages(content, (src) => this.uploadImageByUrl(src))\n\n const postData = {\n title: article.title,\n tags: [],\n text: content,\n object_id: '',\n type: 'article',\n }\n\n const response = await this.runtime.fetch('https://segmentfault.com/gateway/draft', {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n token: this.sessionToken,\n accept: '*/*',\n },\n body: JSON.stringify(postData),\n })\n\n // 处理异常响应\n const text = await response.text()\n if (text === 'Unauthorized' || text.includes('禁言') || text.includes('锁定')) {\n throw new Error(text === 'Unauthorized' ? '未授权' : text)\n }\n\n let res\n try {\n res = JSON.parse(text)\n } catch {\n throw new Error('发布失败: ' + text)\n }\n\n // 处理数组格式响应 [1, \"error_message\"]\n if (Array.isArray(res)) {\n if (res[0] === 1) {\n throw new Error(res[1] || '发布失败')\n }\n // [0, data] 成功格式\n const data = res[1]\n if (data?.id) {\n return {\n platform: this.meta.id,\n success: true,\n postId: data.id,\n postUrl: `https://segmentfault.com/write?draftId=${data.id}`,\n timestamp: now,\n }\n }\n }\n\n if (!res.id) {\n // 尝试多种错误字段\n const errorMsg = res.message || res.msg || res.error || res.errMsg || JSON.stringify(res)\n throw new Error(errorMsg)\n }\n\n return {\n platform: this.meta.id,\n success: true,\n postId: res.id,\n postUrl: `https://segmentfault.com/write?draftId=${res.id}`,\n timestamp: now,\n }\n }).catch((error) => ({\n platform: this.meta.id,\n success: false,\n error: (error as Error).message,\n timestamp: now,\n }))\n }\n}\n","/**\n * Markdown 压缩包下载适配器\n *\n * 将文章导出为 Markdown + 图片的 ZIP 压缩包:\n * - article.md: Markdown 文件\n * - images/: 图片目录,所有图片使用相对路径引用\n *\n * 使用 chrome.downloads API 直接触发下载,避免大文件传递问题\n */\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { CodeAdapter } from '../code-adapter'\nimport { htmlToMarkdown } from '../../lib/turndown'\nimport { createLogger } from '../../lib/logger'\nimport { parseMarkdownImages } from '../../lib/markdown-images'\nimport JSZip from 'jszip'\n\nconst logger = createLogger('ZipDownload')\n\nexport class ZipDownloadAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'zip-download',\n name: 'Markdown 压缩包',\n icon: 'https://cdn-icons-png.flaticon.com/512/337/337946.png',\n homepage: '',\n capabilities: ['article'],\n }\n\n /**\n * 本地导出不需要认证\n */\n async checkAuth(): Promise<AuthResult> {\n return {\n isAuthenticated: true,\n username: '本地下载',\n }\n }\n\n /**\n * 导出为 ZIP 压缩包\n */\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n try {\n const zip = new JSZip()\n const imgFolder = zip.folder('images')!\n\n // 获取 Markdown 内容\n let markdown = article.markdown || htmlToMarkdown(article.html || '')\n\n if (!markdown.trim()) {\n return this.createResult(false, {\n error: '文章内容为空',\n })\n }\n\n // 添加标题\n const title = article.title || '未命名文章'\n if (!markdown.startsWith('# ')) {\n markdown = `# ${title}\\n\\n${markdown}`\n }\n\n // 处理图片:下载并替换为相对路径\n const { processedMarkdown, imageCount } = await this.processImagesForZip(\n markdown,\n imgFolder,\n options?.onImageProgress\n )\n\n // 写入 Markdown 文件\n zip.file('article.md', processedMarkdown)\n\n // 生成 ZIP Blob\n const blob = await zip.generateAsync({\n type: 'blob',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n })\n\n // 使用 runtime.downloads 下载\n const filename = this.sanitizeFilename(title) + '.zip'\n\n if (!this.runtime.downloads) {\n return this.createResult(false, {\n error: '当前环境不支持下载功能',\n })\n }\n\n const downloadId = await this.runtime.downloads.download(blob, filename, true)\n logger.info(`Download started: ${filename}, id: ${downloadId}`)\n\n return this.createResult(true, {\n postId: String(downloadId),\n postUrl: '', // 本地下载,无链接\n message: `已下载 ${filename}(${imageCount} 张图片)`,\n })\n } catch (error) {\n logger.error('ZIP download failed:', error)\n return this.createResult(false, {\n error: (error as Error).message,\n })\n }\n }\n\n /**\n * 处理 Markdown 中的图片:下载并添加到 ZIP\n */\n private async processImagesForZip(\n markdown: string,\n imgFolder: JSZip,\n onProgress?: (current: number, total: number) => void\n ): Promise<{ processedMarkdown: string; imageCount: number }> {\n const matches = parseMarkdownImages(markdown)\n\n if (matches.length === 0) {\n return { processedMarkdown: markdown, imageCount: 0 }\n }\n\n logger.info(`Found ${matches.length} images to process`)\n\n // 生成时间戳前缀(精确到秒)\n const timestamp = Math.floor(Date.now() / 1000)\n\n let processedMarkdown = markdown\n let imageIndex = 0\n let completed = 0\n const urlToFilename = new Map<string, string>() // 去重:相同 URL 使用相同文件名\n\n const uniqueUrls: string[] = []\n for (const { src } of matches) {\n if (src.startsWith('data:')) {\n logger.debug('Skipping data URI image')\n continue\n }\n if (!urlToFilename.has(src)) {\n urlToFilename.set(src, '')\n uniqueUrls.push(src)\n }\n }\n\n if (uniqueUrls.length === 0) {\n return { processedMarkdown: markdown, imageCount: 0 }\n }\n\n const downloadOne = async (src: string) => {\n try {\n const response = await this.runtime.fetch(src, {\n credentials: 'omit',\n })\n\n if (!response.ok) {\n logger.warn(`Failed to download image: ${src}, status: ${response.status}`)\n return\n }\n\n const blob = await response.blob()\n const ext = this.getImageExtension(src, blob.type)\n imageIndex++\n const filename = `image_${timestamp}_${String(imageIndex).padStart(3, '0')}.${ext}`\n\n imgFolder.file(filename, blob)\n urlToFilename.set(src, filename)\n\n logger.debug(`Downloaded image: ${filename}`)\n } catch (error) {\n logger.warn(`Failed to download image: ${src}`, error)\n } finally {\n completed++\n onProgress?.(completed, uniqueUrls.length)\n }\n }\n\n const runPool = async (limit: number) => {\n let cursor = 0\n const workers = Array.from({ length: Math.min(limit, uniqueUrls.length) }, async () => {\n while (cursor < uniqueUrls.length) {\n const current = cursor\n cursor++\n await downloadOne(uniqueUrls[current])\n }\n })\n await Promise.all(workers)\n }\n\n await runPool(4)\n\n for (const { full, alt, src } of matches) {\n const filename = urlToFilename.get(src)\n if (filename) {\n processedMarkdown = processedMarkdown.replace(full, ``)\n }\n }\n\n return {\n processedMarkdown,\n imageCount: Array.from(urlToFilename.values()).filter(Boolean).length,\n }\n }\n\n /**\n * 获取图片扩展名\n */\n private getImageExtension(url: string, mimeType: string): string {\n // 优先从 MIME 类型获取\n const mimeMap: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/svg+xml': 'svg',\n 'image/bmp': 'bmp',\n 'image/x-icon': 'ico',\n }\n\n if (mimeType && mimeMap[mimeType]) {\n return mimeMap[mimeType]\n }\n\n // 从 URL 提取\n const urlMatch = url.match(/\\.(png|jpe?g|gif|webp|svg|bmp|ico)(?:\\?|$)/i)\n if (urlMatch) {\n return urlMatch[1].toLowerCase().replace('jpeg', 'jpg')\n }\n\n // 默认 png\n return 'png'\n }\n\n /**\n * 清理文件名,移除非法字符\n */\n private sanitizeFilename(name: string): string {\n return name\n .replace(/[<>:\"/\\\\|?*]/g, '_') // Windows 非法字符\n .replace(/[\\x00-\\x1f]/g, '') // 控制字符\n .replace(/\\.+$/, '') // 末尾的点\n .trim()\n .slice(0, 200) // 限制长度\n || 'article'\n }\n}\n","/**\n * 东方财富适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from \"../code-adapter\";\nimport type {\n Article,\n AuthResult,\n SyncResult,\n PlatformMeta,\n} from \"../../types\";\nimport type { PublishOptions } from \"../types\";\nimport { createLogger } from \"../../lib/logger\";\n\nconst logger = createLogger(\"Eastmoney\");\n\ninterface UploadResponse {\n code: number;\n message: string;\n data?: {\n url: string;\n id: string;\n };\n}\n\ninterface DraftApiResponse {\n RRquestSuccess: boolean;\n RCode: number;\n RMsg?: string;\n RData: string;\n}\n\ninterface DraftResult {\n error_code?: number;\n draft_id?: string;\n me?: string;\n}\n\nexport class EastmoneyAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: \"eastmoney\",\n name: \"东方财富\",\n icon: \"https://mp.eastmoney.com/collect/pc_article/favicon.ico\",\n homepage: \"https://mp.eastmoney.com\",\n capabilities: [\"article\", \"draft\", \"image_upload\", \"cover\"],\n };\n\n /** 预处理配置 */\n readonly preprocessConfig = {\n outputFormat: \"html\" as const,\n removeComments: true,\n removeSpecialTags: true,\n processCodeBlocks: true,\n convertSectionToDiv: true,\n removeEmptyLines: true,\n removeEmptyDivs: true,\n removeNestedEmptyContainers: true,\n unwrapSingleChildContainers: true,\n unwrapNestedFigures: true,\n removeTrailingBr: true,\n removeDataAttributes: true,\n removeSrcset: true,\n removeSizes: true,\n compactHtml: true,\n };\n\n private ctoken: string = \"\";\n private utoken: string = \"\";\n private deviceId: string = \"\";\n\n /** 获取或生成持久化 deviceId(32位大写 hex) */\n private async getDeviceId(): Promise<string> {\n if (this.deviceId) return this.deviceId;\n const stored = await this.runtime.storage.get<string>(\"eastmoney_deviceId\");\n if (stored) {\n this.deviceId = stored;\n return this.deviceId;\n }\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n this.deviceId = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\").toUpperCase())\n .join(\"\");\n await this.runtime.storage.set(\"eastmoney_deviceId\", this.deviceId);\n return this.deviceId;\n }\n\n /** API Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: \"*://mp.eastmoney.com/*\",\n headers: {\n Origin: \"https://mp.eastmoney.com\",\n HOST: \"emfront.eastmoney.com\",\n },\n resourceTypes: [\"xmlhttprequest\"],\n },\n ];\n\n async checkAuth(): Promise<AuthResult> {\n try {\n await this.fetchToken();\n\n const response = await this.runtime.fetch(\n `https://caifuhaoapi.eastmoney.com/api/v2/getauthorinfo?platform=&ctoken=${this.ctoken}&utoken=${this.utoken}`,\n {\n method: \"GET\",\n credentials: \"include\",\n headers: { \"x-requested-with\": \"fetch\" },\n },\n );\n\n const data = (await response.json()) as {\n Success: number;\n Result?: {\n accountId?: string;\n accountName?: string;\n portrait?: string;\n };\n };\n\n if (data.Success === 1 && data.Result?.accountId) {\n return {\n isAuthenticated: true,\n userId: data.Result.accountId,\n username: data.Result.accountName,\n avatar: data.Result.portrait,\n };\n }\n\n return { isAuthenticated: false };\n } catch (error) {\n logger.debug(\"checkAuth: not logged in -\", error);\n return { isAuthenticated: false, error: (error as Error).message };\n }\n }\n\n /** 从 cookie 读取 token */\n private async fetchToken(): Promise<void> {\n if (!this.runtime.getCookie) {\n throw new Error(\"Cookie API 不可用,请先登录东方财富\");\n }\n\n const ctoken = await this.runtime.getCookie(\".eastmoney.com\", \"ct\");\n const utoken = await this.runtime.getCookie(\".eastmoney.com\", \"ut\");\n\n if (!ctoken || !utoken) {\n throw new Error(\"未检测到登录信息,请先登录东方财富\");\n }\n\n this.ctoken = ctoken;\n this.utoken = utoken;\n }\n\n async publish(\n article: Article,\n options?: PublishOptions,\n ): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n await this.fetchToken();\n logger.info(\"Starting publish to eastmoney...\");\n\n // 1. 创建空草稿,获取 draft_id\n const draftId = await this.createDraft(article.title);\n logger.debug(\"Draft created:\", draftId);\n\n // 2. 处理图片(基于预处理后的 HTML)\n const content = await this.processImages(\n article.html || \"\",\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: [\"gbres.dfcfw.com\"],\n onProgress: options?.onImageProgress,\n },\n );\n\n // 3. 更新草稿内容\n await this.updateDraft(draftId, article.title, content);\n logger.debug(\"Draft updated\");\n\n const draftUrl = `https://mp.eastmoney.com/collect/pc_article/index.html#/?id=${draftId}`;\n\n return this.createResult(true, {\n postId: draftId,\n postUrl: draftUrl,\n });\n }).catch((error) =>\n this.createResult(false, {\n error: (error as Error).message,\n }),\n );\n }\n\n /** 构造 API 参数 */\n private async buildParm(params: {\n draftid?: string;\n title: string;\n text: string;\n }): Promise<object[]> {\n const deviceid = await this.getDeviceId();\n return [\n { ip: \"$IP$\" },\n { deviceid },\n { version: \"100\" },\n { plat: \"web\" },\n { product: \"CFH\" },\n { ctoken: this.ctoken },\n { utoken: this.utoken },\n { draftid: params.draftid ?? \"\" },\n { drafttype: \"0\" },\n { type: \"0\" },\n { title: encodeURIComponent(params.title) },\n { text: encodeURIComponent(params.text) },\n { columns: \"2\" },\n { cover: \"\" },\n { issimplevideo: \"0\" },\n { videos: \"\" },\n { vods: \"\" },\n { isoriginal: \"0\" },\n { tgProduct: \"\" },\n { spcolumns: \"\" },\n { textsource: \"0\" },\n { replyauthority: \"\" },\n { modules: encodeURIComponent(\"[]\") },\n ];\n }\n\n /** 调用草稿 API */\n private async callDraftApi(parm: object[], draftId?: string): Promise<DraftResult> {\n const pageUrl = draftId\n ? `https://mp.eastmoney.com/collect/pc_article/index.html#/?id=${draftId}`\n : \"https://mp.eastmoney.com/collect/pc_article/index.html#/\";\n\n const body = JSON.stringify({\n pageUrl,\n path: \"draft/api/Article/SaveDraft\",\n parm: JSON.stringify(parm),\n });\n\n const response = await this.runtime.fetch(\n \"https://emfront.eastmoney.com/apifront/Tran/GetData?platform=\",\n {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n },\n );\n\n const responseText = await response.text();\n logger.debug(\n \"Draft API response:\",\n response.status,\n responseText.substring(0, 200),\n );\n\n if (!response.ok) {\n throw new Error(`草稿 API 请求失败: ${response.status}`);\n }\n\n let rawData: DraftApiResponse;\n try {\n rawData = JSON.parse(responseText);\n } catch {\n throw new Error(\"草稿 API 响应不是有效 JSON\");\n }\n\n if (!rawData.RRquestSuccess || rawData.RCode !== 200) {\n throw new Error(`草稿 API 错误: ${rawData.RMsg || \"未知错误\"}`);\n }\n\n let innerData: DraftResult;\n try {\n innerData = JSON.parse(rawData.RData);\n } catch {\n throw new Error(\"无法解析草稿响应数据\");\n }\n\n if (innerData.error_code !== 0) {\n throw new Error(`草稿业务错误: ${innerData.me || \"未知错误\"}`);\n }\n\n return innerData;\n }\n\n private async createDraft(title: string): Promise<string> {\n const parm = await this.buildParm({\n title,\n text: '<div class=\"xeditor_content cfh_web\"></div>',\n });\n const result = await this.callDraftApi(parm);\n if (!result.draft_id) {\n throw new Error(\"创建草稿失败: 响应缺少 draft_id\");\n }\n return result.draft_id;\n }\n\n private async updateDraft(\n draftId: string,\n title: string,\n content: string,\n ): Promise<void> {\n const parm = await this.buildParm({\n draftid: draftId,\n title,\n text: `<div class=\"xeditor_content cfh_web\">${content}</div>`,\n });\n await this.callDraftApi(parm, draftId);\n }\n\n /** URL 上传图片 */\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n // data URI 使用二进制上传\n if (src.startsWith(\"data:\")) {\n logger.debug(\"Detected data URI, using binary upload\");\n const blob = await this.dataUriToBlob(src);\n return this.uploadImageBlob(blob);\n }\n\n // 远程 URL 使用链接上传接口\n const response = await this.runtime.fetch(\n \"https://gbapi.eastmoney.com/iimage/image/byLink?platform=\",\n {\n method: \"PUT\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n noinlist: \"1\",\n linkUrl: src,\n ctoken: this.ctoken,\n utoken: this.utoken,\n }),\n },\n );\n\n const res = (await response.json()) as UploadResponse;\n if (res.code === 200 && res.data?.url) {\n return { url: res.data.url };\n }\n throw new Error(\n `图片上传失败: ${res.message || \"未知错误\"} (code: ${res.code})`,\n );\n }\n\n /** 上传图片 Blob */\n private async uploadImageBlob(file: Blob): Promise<ImageUploadResult> {\n const ext = file.type.split(\"/\")[1] || \"png\";\n const filename = `${Date.now()}.${ext}`;\n\n const formData = new FormData();\n formData.append(\"file\", file, filename);\n formData.append(\"noinlist\", \"1\");\n formData.append(\"utoken\", this.utoken);\n formData.append(\"ctoken\", this.ctoken);\n\n const response = await this.runtime.fetch(\n \"https://gbapi.eastmoney.com/iimage/image?platform=\",\n {\n method: \"POST\",\n credentials: \"include\",\n body: formData,\n },\n );\n\n const res = (await response.json()) as UploadResponse;\n if (res.code === 200 && res.data?.url) {\n return { url: res.data.url };\n }\n throw new Error(\n `图片上传失败: ${res.message || \"未知错误\"} (code: ${res.code})`,\n );\n }\n}\n"],"mappings":"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","names":["logger","logger","logger","logger","logger"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/types.ts","../../src/adapters/base.ts","../../src/adapters/registry.ts","../../src/lib/markdown-to-draft.ts","../../src/adapters/platforms/douban.ts","../../src/adapters/platforms/xueqiu.ts","../../src/adapters/platforms/woshipm.ts","../../src/adapters/platforms/yuque.ts","../../src/adapters/platforms/imooc.ts","../../src/adapters/platforms/oschina.ts","../../src/adapters/platforms/segmentfault.ts","../../src/adapters/platforms/zip-download.ts","../../src/adapters/platforms/eastmoney.ts"],"sourcesContent":["import type {\n Article,\n AuthResult,\n PublishEventHandler,\n PublishStatusRef,\n PublishStatusResult,\n SyncResult,\n PlatformMeta,\n} from '../types'\nimport type { RuntimeInterface } from '../runtime/interface'\n\n/**\n * 输出格式类型\n */\nexport type OutputFormat = 'html' | 'markdown'\n\n/**\n * 预处理配置\n * 每个平台在 adapter 中定义自己需要的预处理选项\n * Content Script 根据这些配置在发送到 Service Worker 前进行预处理\n */\nexport interface PreprocessConfig {\n /** 输出格式: html 或 markdown */\n outputFormat: OutputFormat\n\n /** 移除链接标签,保留文字 */\n removeLinks?: boolean\n /** 保留的链接域名 */\n keepLinkDomains?: string[]\n\n /** 移除 iframe */\n removeIframes?: boolean\n /** 移除 HTML 注释 */\n removeComments?: boolean\n /** 移除微信特殊标签 (mpprofile, qqmusic 等) */\n removeSpecialTags?: boolean\n /** 移除特殊标签时同时移除其父元素(知乎等需要) */\n removeSpecialTagsWithParent?: boolean\n /** 移除 SVG 占位图片 */\n removeSvgImages?: boolean\n\n /** 处理代码块 */\n processCodeBlocks?: boolean\n /** 处理懒加载图片 (data-src → src) */\n processLazyImages?: boolean\n\n /** 移除空元素 (空 p, div, section 等) */\n removeEmptyElements?: boolean\n /** 移除没有有效 src 的 img 标签(src 为空或缺失) */\n removeEmptyImages?: boolean\n /** 移除 data-* 属性 */\n removeDataAttributes?: boolean\n /** 移除 srcset 属性 */\n removeSrcset?: boolean\n /** 移除 sizes 属性 */\n removeSizes?: boolean\n\n /** 将 section 转换为 div */\n convertSectionToDiv?: boolean\n /** 将 section 转换为 p */\n convertSectionToP?: boolean\n\n // 知乎等平台需要的额外处理\n /** 移除段落尾部的 br 标签 */\n removeTrailingBr?: boolean\n /** 解包单一子元素容器 */\n unwrapSingleChildContainers?: boolean\n /** 解包只含单个子 span 的 span(减少过深嵌套) */\n unwrapSingleChildSpans?: boolean\n /** 解包嵌套的 figure 标签 */\n unwrapNestedFigures?: boolean\n /** 展平嵌套的 b/strong 标签(移除内层,保留内容) */\n flattenNestedBold?: boolean\n /** 压缩 HTML(移除标签间空白) */\n compactHtml?: boolean\n\n /** 将表格转换为文本(用 | 分隔列,适用于不支持表格的平台) */\n convertTablesToText?: boolean\n\n /** 保留 <style> 标签(CLI 同步自定义 HTML 时使用) */\n keepStyles?: boolean\n\n // 清理空内容\n /** 移除空行(只含 br 或空白的段落) */\n removeEmptyLines?: boolean\n /** 移除空 div(只含 br 或空白,但保留含图片的) */\n removeEmptyDivs?: boolean\n /** 移除嵌套的空容器 */\n removeNestedEmptyContainers?: boolean\n}\n\n/**\n * 默认预处理配置\n */\nexport const DEFAULT_PREPROCESS_CONFIG: PreprocessConfig = {\n outputFormat: 'html',\n removeIframes: true,\n removeComments: true,\n removeSpecialTags: true,\n removeSvgImages: true,\n processCodeBlocks: true,\n processLazyImages: true,\n removeEmptyElements: true,\n removeDataAttributes: true,\n removeSrcset: true,\n removeSizes: true,\n}\n\n/**\n * 图片上传进度回调\n */\nexport type ImageProgressCallback = (current: number, total: number) => void\n\n/**\n * 发布选项\n */\nexport interface PublishOptions {\n /** 图片上传进度回调 */\n onImageProgress?: ImageProgressCallback\n /** 发布过程事件:扫码、验证码、进度 */\n onEvent?: PublishEventHandler\n}\n\n/**\n * 平台适配器接口\n */\nexport interface PlatformAdapter {\n /** 平台元信息 */\n readonly meta: PlatformMeta\n\n /** 预处理配置 (Content Script 根据此配置预处理内容) */\n readonly preprocessConfig?: Partial<PreprocessConfig>\n\n /** 初始化适配器 */\n init(runtime: RuntimeInterface): Promise<void>\n\n /** 检查认证状态 */\n checkAuth(): Promise<AuthResult>\n\n /** 发布文章 */\n publish(article: Article, options?: PublishOptions): Promise<SyncResult>\n\n /** 查询文章状态 */\n getStatus?(statusRef: PublishStatusRef): Promise<PublishStatusResult>\n\n /** 上传图片 (如果支持) */\n uploadImage?(file: Blob, filename?: string): Promise<string>\n\n /** 获取分类列表 (如果支持) */\n getCategories?(): Promise<Category[]>\n\n /** 获取草稿列表 (如果支持) */\n getDrafts?(): Promise<Draft[]>\n\n /** 更新文章 (如果支持) */\n update?(postId: string, article: Article): Promise<SyncResult>\n\n /** 删除文章 (如果支持) */\n delete?(postId: string): Promise<void>\n}\n\n/**\n * 分类\n */\nexport interface Category {\n id: string\n name: string\n parentId?: string\n}\n\n/**\n * 草稿\n */\nexport interface Draft {\n id: string\n title: string\n updatedAt: number\n}\n\n/**\n * 适配器注册项\n */\nexport interface AdapterRegistryEntry {\n meta: PlatformMeta\n factory: (runtime: RuntimeInterface) => PlatformAdapter\n /** 预处理配置 (Content Script 根据此配置预处理内容) */\n preprocessConfig?: Partial<PreprocessConfig>\n}\n","import type { Article, AuthResult, SyncResult, PlatformMeta } from '../types'\nimport type { RuntimeInterface } from '../runtime/interface'\nimport type { PlatformAdapter } from './types'\n\n/**\n * 适配器基类\n * 提供通用的请求处理和模板解析\n */\nexport abstract class BaseAdapter implements PlatformAdapter {\n abstract readonly meta: PlatformMeta\n protected runtime!: RuntimeInterface\n protected context: Record<string, unknown> = {}\n\n async init(runtime: RuntimeInterface): Promise<void> {\n this.runtime = runtime\n }\n\n abstract checkAuth(): Promise<AuthResult>\n abstract publish(article: Article): Promise<SyncResult>\n\n /**\n * 发送请求\n */\n protected async request<T = unknown>(\n url: string,\n options: RequestInit = {}\n ): Promise<T> {\n const response = await this.runtime.fetch(url, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const contentType = response.headers.get('content-type')\n if (contentType?.includes('application/json')) {\n return response.json()\n }\n\n return response.text() as T\n }\n\n /**\n * 带重试的请求\n */\n protected async requestWithRetry<T = unknown>(\n url: string,\n options: RequestInit = {},\n maxRetries = 3\n ): Promise<T> {\n let lastError: Error | null = null\n\n for (let i = 0; i < maxRetries; i++) {\n try {\n return await this.request<T>(url, options)\n } catch (error) {\n lastError = error as Error\n if (i < maxRetries - 1) {\n await this.delay(1000 * (i + 1))\n }\n }\n }\n\n throw lastError\n }\n\n /**\n * 延迟\n */\n protected delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n }\n\n /**\n * 创建同步结果\n */\n protected createResult(\n success: boolean,\n data?: Partial<SyncResult>\n ): SyncResult {\n return {\n platform: this.meta.id,\n success,\n timestamp: Date.now(),\n ...data,\n }\n }\n}\n","import type { PlatformAdapter, AdapterRegistryEntry, PreprocessConfig } from './types'\nimport { DEFAULT_PREPROCESS_CONFIG } from './types'\nimport type { RuntimeInterface } from '../runtime/interface'\nimport type { PlatformMeta } from '../types'\n\n/**\n * 适配器注册中心\n * 管理所有平台适配器的注册和获取\n */\nclass AdapterRegistry {\n private adapters: Map<string, AdapterRegistryEntry> = new Map()\n private instances: Map<string, PlatformAdapter> = new Map()\n private runtime?: RuntimeInterface\n\n /**\n * 设置运行时\n */\n setRuntime(runtime: RuntimeInterface): void {\n this.runtime = runtime\n // 清空已有实例,等待重新初始化\n this.instances.clear()\n }\n\n /**\n * 注册适配器\n */\n register(entry: AdapterRegistryEntry): void {\n this.adapters.set(entry.meta.id, entry)\n }\n\n /**\n * 批量注册\n */\n registerAll(entries: AdapterRegistryEntry[]): void {\n entries.forEach(entry => this.register(entry))\n }\n\n /**\n * 获取适配器实例\n */\n async get(platformId: string): Promise<PlatformAdapter | null> {\n // 检查缓存\n if (this.instances.has(platformId)) {\n return this.instances.get(platformId)!\n }\n\n // 查找注册项\n const entry = this.adapters.get(platformId)\n if (!entry) {\n return null\n }\n\n if (!this.runtime) {\n throw new Error('Runtime not set. Call setRuntime() first.')\n }\n\n // 创建并初始化实例\n const adapter = entry.factory(this.runtime)\n await adapter.init(this.runtime)\n this.instances.set(platformId, adapter)\n\n return adapter\n }\n\n /**\n * 获取所有平台元信息\n */\n getAllMeta(): PlatformMeta[] {\n return Array.from(this.adapters.values()).map(entry => entry.meta)\n }\n\n /**\n * 检查平台是否已注册\n */\n has(platformId: string): boolean {\n return this.adapters.has(platformId)\n }\n\n /**\n * 获取已注册的平台 ID 列表\n */\n getRegisteredIds(): string[] {\n return Array.from(this.adapters.keys())\n }\n\n /**\n * 清空注册\n */\n clear(): void {\n this.adapters.clear()\n this.instances.clear()\n }\n\n /**\n * 获取平台的预处理配置\n */\n getPreprocessConfig(platformId: string): PreprocessConfig {\n const entry = this.adapters.get(platformId)\n return {\n ...DEFAULT_PREPROCESS_CONFIG,\n ...(entry?.preprocessConfig || {}),\n }\n }\n\n /**\n * 获取多个平台的预处理配置\n */\n getPreprocessConfigs(platformIds: string[]): Record<string, PreprocessConfig> {\n const configs: Record<string, PreprocessConfig> = {}\n for (const id of platformIds) {\n configs[id] = this.getPreprocessConfig(id)\n }\n return configs\n }\n}\n\n/**\n * 全局适配器注册中心实例\n */\nexport const adapterRegistry = new AdapterRegistry()\n\n/**\n * 注册适配器的便捷函数\n */\nexport function registerAdapter(entry: AdapterRegistryEntry): void {\n adapterRegistry.register(entry)\n}\n\n/**\n * 获取适配器的便捷函数\n */\nexport async function getAdapter(platformId: string): Promise<PlatformAdapter | null> {\n return adapterRegistry.get(platformId)\n}\n\n/**\n * 获取平台的预处理配置\n */\nexport function getPreprocessConfig(platformId: string): PreprocessConfig {\n return adapterRegistry.getPreprocessConfig(platformId)\n}\n\n/**\n * 获取多个平台的预处理配置\n */\nexport function getPreprocessConfigs(platformIds: string[]): Record<string, PreprocessConfig> {\n return adapterRegistry.getPreprocessConfigs(platformIds)\n}\n","/**\n * Markdown to Draft.js 转换\n * 用于豆瓣等使用 Draft.js 编辑器的平台\n */\n\n// @ts-ignore - markdown-draft-js 没有类型定义\nimport { markdownToDraft as mdToDraft } from 'markdown-draft-js'\nimport { Remarkable } from 'remarkable'\n\n/**\n * 图片数据接口 - 豆瓣需要完整的图片信息\n */\nexport interface DoubanImageData {\n id: string\n url: string\n thumb: string\n width?: number\n height?: number\n file_name?: string\n file_size?: number\n}\n\n/**\n * 豆瓣图片 Block 解析器 - 处理 ![]() 格式的图片\n */\nconst ImageRegexp = /^!\\[([^\\]]*)]\\s*\\(([^)\"]+)( \"([^)\"]+)\")?\\)/\n\nconst imageBlockPlugin = (remarkable: Remarkable) => {\n // @ts-ignore - remarkable types incomplete\n remarkable.block.ruler.before('paragraph', 'image', (state: any, startLine: number, endLine: number, silent: boolean) => {\n const pos = state.bMarks[startLine] + state.tShift[startLine]\n const max = state.eMarks[startLine]\n\n if (pos >= max) return false\n if (!state.src) return false\n if (state.src[pos] !== '!') return false\n\n const match = ImageRegexp.exec(state.src.slice(pos))\n if (!match) return false\n\n if (!silent) {\n state.tokens.push({\n type: 'image_open',\n src: match[2],\n alt: match[1],\n lines: [startLine, state.line],\n level: state.level\n })\n state.tokens.push({\n type: 'image_close',\n level: state.level\n })\n }\n\n state.line = startLine + 1\n return true\n })\n}\n\n/**\n * 将 Markdown 转换为 Draft.js 格式 (豆瓣专用)\n * @param markdown Markdown 内容\n * @param imageDataMap 图片 URL 到完整数据的映射\n * @returns Draft.js JSON 字符串\n */\nexport function markdownToDraft(markdown: string, imageDataMap: Map<string, DoubanImageData> = new Map()): string {\n // 保证图片换行\n const processedMarkdown = markdown.split('\\n').map(line => {\n const imageBlocks = line.split('![]')\n return imageBlocks.length > 1 ? imageBlocks.join('\\n![]') : line\n }).join('\\n')\n\n let keyCounter = 0\n const generateUniqueKey = () => keyCounter++\n\n const draftState = mdToDraft(processedMarkdown, {\n remarkablePlugins: [imageBlockPlugin],\n blockTypes: {\n image_open: function (item: any) {\n const key = generateUniqueKey()\n const blockEntities: Record<number, any> = {}\n\n // 解析 ?# 格式获取原始 URL 和 ID\n const sourcePair = item.src ? item.src.split('?#') : ['', '']\n const rawSrc = sourcePair[0]\n const sourceId = sourcePair[1] || ''\n\n // 从 imageDataMap 获取完整图片数据\n const imgData = imageDataMap.get(item.src) || imageDataMap.get(rawSrc)\n\n const imageTemplate = imgData ? {\n id: imgData.id,\n src: imgData.url,\n thumb: imgData.thumb,\n url: imgData.url,\n width: imgData.width,\n height: imgData.height,\n file_name: imgData.file_name,\n file_size: imgData.file_size,\n } : {\n id: sourceId,\n src: rawSrc,\n thumb: rawSrc,\n url: rawSrc,\n }\n\n blockEntities[key] = {\n type: 'IMAGE',\n mutability: 'IMMUTABLE',\n data: imageTemplate,\n }\n\n return {\n type: 'atomic',\n blockEntities: blockEntities,\n inlineStyleRanges: [],\n entityRanges: [{ offset: 0, length: 1, key: key }],\n text: ' ',\n }\n }\n },\n blockEntities: {\n image: function (item: any) {\n const sourcePair = item.src ? item.src.split('?#') : ['', '']\n const rawSrc = sourcePair[0]\n const sourceId = sourcePair[1] || ''\n\n // 从 imageDataMap 获取完整图片数据\n const imgData = imageDataMap.get(item.src) || imageDataMap.get(rawSrc)\n\n if (imgData) {\n return {\n type: 'IMAGE',\n mutability: 'IMMUTABLE',\n data: {\n id: imgData.id,\n src: imgData.url,\n thumb: imgData.thumb,\n url: imgData.url,\n width: imgData.width,\n height: imgData.height,\n }\n }\n }\n\n return {\n type: 'IMAGE',\n mutability: 'IMMUTABLE',\n data: {\n id: sourceId,\n src: rawSrc,\n thumb: rawSrc,\n url: rawSrc,\n }\n }\n }\n }\n })\n\n // 将 block.blockEntities 合并到顶层 entityMap (参考 mtd.js)\n if (draftState.blocks) {\n for (const block of draftState.blocks) {\n if (block.blockEntities) {\n Object.assign(draftState.entityMap, block.blockEntities)\n delete block.blockEntities\n }\n }\n }\n\n return JSON.stringify(draftState)\n}\n","/**\n * 豆瓣适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { DoubanImageData } from '../../lib'\nimport type { PublishOptions } from '../types'\nimport { markdownToDraft } from '../../lib'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Douban')\n\ninterface DoubanFormData {\n note_id: string\n ck: string\n}\n\ninterface DoubanPostParams {\n siteCookie: {\n value: string\n }\n}\n\nexport class DoubanAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'douban',\n name: '豆瓣',\n icon: 'https://www.douban.com/favicon.ico',\n homepage: 'https://www.douban.com/note/create',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 豆瓣使用 Markdown 格式 (转换为 Draft.js) */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n private username: string = ''\n private avatar: string = ''\n private formData: DoubanFormData | null = null\n private postParams: DoubanPostParams | null = null\n\n /** 豆瓣 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://www.douban.com/*',\n headers: {\n 'Origin': 'https://www.douban.com',\n 'Referer': 'https://www.douban.com',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch(\n 'https://www.douban.com/note/create',\n {\n method: 'GET',\n credentials: 'include',\n }\n )\n\n const html = await response.text()\n\n // 解析页面中的 JavaScript 变量\n const userNameMatch = html.match(/_USER_NAME\\s*=\\s*['\"]([^'\"]+)['\"]/)\n const userAvatarMatch = html.match(/_USER_AVATAR\\s*=\\s*['\"]([^'\"]+)['\"]/)\n const noteIdMatch = html.match(/name=\"note_id\"\\s+value=\"(\\d+)\"/)\n const ckMatch = html.match(/name=\"ck\"\\s+value=\"([^\"]+)\"/)\n\n // 解析 _POST_PARAMS\n const postParamsMatch = html.match(/_POST_PARAMS\\s*=\\s*(\\{[\\s\\S]*?\\});/)\n\n if (!userNameMatch || !noteIdMatch || !ckMatch) {\n return { isAuthenticated: false }\n }\n\n this.username = userNameMatch[1]\n this.avatar = userAvatarMatch ? userAvatarMatch[1] : ''\n this.formData = {\n note_id: noteIdMatch[1],\n ck: ckMatch[1],\n }\n\n // 解析 _POST_PARAMS 获取 upload_auth_token\n if (postParamsMatch) {\n try {\n // 简化解析,只提取 siteCookie.value\n const siteCookieMatch = postParamsMatch[1].match(/siteCookie[^}]*value\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (siteCookieMatch) {\n this.postParams = {\n siteCookie: { value: siteCookieMatch[1] }\n }\n }\n } catch (e) {\n logger.warn('Failed to parse _POST_PARAMS:', e)\n }\n }\n\n logger.debug('Auth info:', {\n username: this.username,\n noteId: this.formData.note_id,\n hasPostParams: !!this.postParams,\n })\n\n return {\n isAuthenticated: true,\n userId: this.username,\n username: this.username,\n avatar: this.avatar,\n }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', error)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n // 1. 确保已登录\n if (!this.formData) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('请先登录豆瓣')\n }\n }\n\n // Use pre-processed markdown content directly\n let content = article.markdown || ''\n\n // Process images - collect full image data\n const imageDataMap = new Map<string, DoubanImageData>()\n content = await this.processImages(\n content,\n async (src) => {\n const result = await this.uploadImageWithFullData(src)\n // 保存完整图片数据,用 newUrl 作为 key\n imageDataMap.set(result.url, result.imageData)\n return result\n },\n {\n skipPatterns: ['doubanio.com', 'douban.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n // Markdown to Draft.js format (pass in image data)\n const draftContent = markdownToDraft(content, imageDataMap)\n\n // 6. 保存草稿\n const response = await this.runtime.fetch(\n 'https://www.douban.com/j/note/autosave',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n is_rich: '1',\n note_id: this.formData!.note_id,\n note_title: article.title,\n note_text: draftContent,\n introduction: '',\n note_privacy: 'P',\n cannot_reply: '',\n author_tags: '',\n accept_donation: '',\n donation_notice: '',\n is_original: '',\n ck: this.formData!.ck,\n }),\n }\n )\n\n const res = await response.json() as { url?: string; r?: number }\n logger.debug('Save response:', res)\n\n // 豆瓣草稿只能在 /note/create 页面查看\n const draftUrl = 'https://www.douban.com/note/create'\n\n return this.createResult(true, {\n postId: this.formData!.note_id,\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n /**\n * 上传图片并返回完整数据\n */\n private async uploadImageWithFullData(src: string): Promise<ImageUploadResult & { imageData: DoubanImageData }> {\n if (!this.formData || !this.postParams) {\n throw new Error('未获取上传凭证')\n }\n\n // 1. 下载图片\n const imageResponse = await fetch(src)\n if (!imageResponse.ok) {\n throw new Error('图片下载失败: ' + src)\n }\n const imageBlob = await imageResponse.blob()\n\n // 2. 上传到豆瓣\n const formData = new FormData()\n formData.append('note_id', this.formData.note_id)\n formData.append('image_file', imageBlob, 'image.jpg')\n formData.append('ck', this.formData.ck)\n formData.append('upload_auth_token', this.postParams.siteCookie.value)\n\n const uploadResponse = await this.runtime.fetch(\n 'https://www.douban.com/j/note/add_photo',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await uploadResponse.json() as {\n photo?: {\n id: string\n url: string\n thumb: string\n width: number\n height: number\n file_name: string\n file_size: number\n }\n }\n\n logger.debug('Image upload response:', res)\n\n if (!res.photo?.url) {\n throw new Error('图片上传失败')\n }\n\n const photo = res.photo\n\n // 返回带完整图片数据\n return {\n url: photo.url,\n imageData: {\n id: photo.id,\n url: photo.url,\n thumb: photo.thumb,\n width: photo.width,\n height: photo.height,\n file_name: photo.file_name,\n file_size: photo.file_size,\n }\n }\n }\n}\n","/**\n * 雪球适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport { Remarkable } from 'remarkable'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Xueqiu')\n\ninterface XueqiuUser {\n id: string\n screen_name: string\n photo_domain: string\n profile_image_url: string\n}\n\nexport class XueqiuAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'xueqiu',\n name: '雪球',\n icon: 'https://xqdoc.imedao.com/17aebcfb84a145d33fc18679.ico',\n homepage: 'https://mp.xueqiu.com/writeV2',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 雪球使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n // doPreFilter + processDocCode (旧版)\n removeSpecialTags: true,\n removeSpecialTagsWithParent: true,\n processCodeBlocks: true,\n }\n\n private currentUser: XueqiuUser | null = null\n\n /** 雪球 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://mp.xueqiu.com/xq/*',\n headers: {\n 'Origin': 'https://mp.xueqiu.com',\n 'Referer': 'https://mp.xueqiu.com/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch(\n 'https://mp.xueqiu.com/writeV2',\n {\n method: 'GET',\n credentials: 'include',\n }\n )\n\n const html = await response.text()\n\n // 解析 window.UOM_CURRENTUSER - 新版格式\n const userMatch = html.match(/window\\.UOM_CURRENTUSER\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*<\\/script>/)\n if (!userMatch) {\n return { isAuthenticated: false }\n }\n\n try {\n const state = JSON.parse(userMatch[1])\n const { currentUser } = state\n\n if (!currentUser?.id) {\n return { isAuthenticated: false }\n }\n\n this.currentUser = currentUser\n\n const avatar = currentUser.photo_domain && currentUser.profile_image_url\n ? `https:${currentUser.photo_domain}${currentUser.profile_image_url.split(',')[0]}`\n : ''\n\n return {\n isAuthenticated: true,\n userId: String(currentUser.id),\n username: currentUser.screen_name,\n avatar,\n }\n } catch (e) {\n logger.error(' Failed to parse user data:', e)\n return { isAuthenticated: false }\n }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', error)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n // 1. 确保已登录\n if (!this.currentUser) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('请先登录雪球')\n }\n }\n\n // Use pre-processed markdown content directly\n let markdown = article.markdown || ''\n\n // Process images in markdown\n markdown = await this.processImages(\n markdown,\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: ['xueqiu.com', 'imedao.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n // Convert Markdown to simplified HTML (Xueqiu format)\n const md = new Remarkable({\n html: true,\n breaks: true,\n })\n\n // 自定义渲染规则适配雪球格式\n // 所有标题都转为 h4\n md.renderer.rules.heading_open = () => '<h4>'\n md.renderer.rules.heading_close = () => '</h4>'\n\n // strong -> b\n md.renderer.rules.strong_open = () => '<b>'\n md.renderer.rules.strong_close = () => '</b>'\n\n // em -> i\n md.renderer.rules.em_open = () => '<i>'\n md.renderer.rules.em_close = () => '</i>'\n\n // 列表 - 移除列表包装,列表项内的 p 标签由 remarkable 自动处理\n md.renderer.rules.bullet_list_open = () => ''\n md.renderer.rules.bullet_list_close = () => ''\n md.renderer.rules.ordered_list_open = () => ''\n md.renderer.rules.ordered_list_close = () => ''\n md.renderer.rules.list_item_open = () => ''\n md.renderer.rules.list_item_close = () => ''\n\n // 移除 hr\n md.renderer.rules.hr = () => ''\n\n // 图片添加 class\n md.renderer.rules.image = (tokens: any[], idx: number) => {\n const src = tokens[idx].src || ''\n const alt = tokens[idx].alt || ''\n return `<img src=\"${src}\" alt=\"${alt}\" class=\"ke_img\">`\n }\n\n let rendered = md.render(markdown)\n\n // Clean up: remove empty p tags and excessive newlines\n rendered = rendered\n .replace(/<p>\\s*<\\/p>/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n\n const content = rendered\n\n // 4. 保存草稿\n const formData = new URLSearchParams({\n text: content,\n title: article.title,\n cover_pic: '',\n flags: 'false',\n original_event: '',\n status_id: '',\n legal_user_visible: 'false',\n is_private: 'false',\n })\n\n const response = await this.runtime.fetch(\n 'https://mp.xueqiu.com/xq/statuses/draft/save.json',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: formData,\n }\n )\n\n const res = await response.json() as {\n id?: string | number\n error_description?: string\n }\n\n logger.debug(' Save response:', res)\n\n if (!res.id) {\n throw new Error(res.error_description || '保存失败')\n }\n\n const postId = res.id\n const draftUrl = `https://mp.xueqiu.com/write/draft/${postId}`\n\n return this.createResult(true, {\n postId: String(postId),\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n /**\n * 通过 URL 上传图片\n */\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n // 1. 下载图片\n const imageResponse = await fetch(src)\n if (!imageResponse.ok) {\n throw new Error('图片下载失败: ' + src)\n }\n const imageBlob = await imageResponse.blob()\n\n // 2. 上传到雪球\n const formData = new FormData()\n formData.append('file', imageBlob, 'image.jpg')\n\n const uploadResponse = await this.runtime.fetch(\n 'https://mp.xueqiu.com/xq/photo/upload.json',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await uploadResponse.json() as {\n url?: string\n filename?: string\n }\n\n logger.debug(' Image upload response:', res)\n\n if (!res.url || !res.filename) {\n throw new Error('图片上传失败')\n }\n\n // 雪球返回的 url 是 //开头,需要加 https:\n const fullUrl = res.url.startsWith('//') ? `https:${res.url}/${res.filename}` : `${res.url}/${res.filename}`\n\n return {\n url: fullUrl,\n }\n }\n}\n","/**\n * 人人都是产品经理 (woshipm.com) 适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Woshipm')\n\nexport class WoshipmAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'woshipm',\n name: '人人都是产品经理',\n icon: 'https://www.woshipm.com/favicon.ico',\n homepage: 'https://www.woshipm.com',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 人人都是产品经理使用 HTML 格式 */\n readonly preprocessConfig = {\n outputFormat: 'html' as const,\n removeEmptyLines: true,\n }\n\n private jltoken: string = ''\n\n /** 人人都是产品经理 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://woshipm.com/wp-admin/admin-ajax.php*',\n headers: { 'X-Requested-With': 'XMLHttpRequest' },\n resourceTypes: ['xmlhttprequest'],\n },\n {\n urlFilter: '*://woshipm.com/api2/*',\n headers: { 'X-Requested-With': 'XMLHttpRequest' },\n resourceTypes: ['xmlhttprequest'],\n },\n {\n urlFilter: '*://woshipm.com/tensorflow/upyun/upload*',\n headers: { 'X-Requested-With': 'XMLHttpRequest' },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n async checkAuth(): Promise<AuthResult> {\n try {\n // 1. 先获取用户页面以获取 uid\n const pageResponse = await this.runtime.fetch('https://www.woshipm.com/writing', {\n method: 'GET',\n credentials: 'include',\n })\n\n const pageText = await pageResponse.text()\n\n // 从页面提取 jltoken: \"jltoken\":\"xxx\"\n const jltokenMatch = pageText.match(/\"jltoken\"\\s*:\\s*\"([^\"]+)\"/)\n if (jltokenMatch) {\n this.jltoken = jltokenMatch[1]\n logger.debug('Found jltoken')\n }\n\n // 从页面提取 uid: var userSettings = {\"url\":\"\\/\",\"uid\":\"1585\",...}\n const uidMatch = pageText.match(/var\\s+userSettings\\s*=\\s*\\{[^}]*\"uid\"\\s*:\\s*\"(\\d+)\"/)\n if (!uidMatch) {\n return { isAuthenticated: false }\n }\n\n const uid = uidMatch[1]\n\n // 2. 调用 profile API 验证登录状态\n const response = await this.runtime.fetch(\n `https://www.woshipm.com/api2/user/profile?uid=${uid}`,\n {\n method: 'GET',\n credentials: 'include',\n headers: {\n 'X-Requested-With': 'XMLHttpRequest',\n },\n }\n )\n\n const data = await response.json() as {\n CODE?: number\n RESULT?: {\n userInfoVo?: {\n uid?: number\n nickName?: string\n avartar?: string // API typo: avartar instead of avatar\n }\n }\n }\n\n if (data.CODE === 200 && data.RESULT?.userInfoVo?.uid) {\n return {\n isAuthenticated: true,\n userId: String(data.RESULT.userInfoVo.uid),\n username: data.RESULT.userInfoVo.nickName,\n avatar: data.RESULT.userInfoVo.avartar,\n }\n }\n\n return { isAuthenticated: false }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', error)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n // 1. 使用预处理好的 HTML(Content Script 已处理代码块、图片、特殊标签等)\n // 人人都是产品经理使用 HTML 格式\n let content = article.html || ''\n\n // 2. 处理图片\n content = await this.processImages(\n content,\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: ['woshipm.com', 'image.woshipm.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n // 4. 创建草稿\n const createResponse = await this.runtime.fetch(\n 'https://www.woshipm.com/wp-admin/admin-ajax.php',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'X-Requested-With': 'XMLHttpRequest',\n },\n body: new URLSearchParams({\n action: 'add_draft',\n post_title: article.title,\n post_content: content,\n }),\n }\n )\n\n // 检查响应状态和内容\n const responseText = await createResponse.text()\n logger.debug('Create draft response:', createResponse.status, responseText.substring(0, 300))\n\n if (!createResponse.ok) {\n throw new Error(`创建草稿失败: ${createResponse.status} - ${responseText}`)\n }\n\n let createData: { post_id?: string | number; url?: string; success?: boolean; error?: string }\n try {\n createData = JSON.parse(responseText)\n } catch {\n throw new Error(`创建草稿失败: 响应不是有效 JSON - ${responseText.substring(0, 100)}`)\n }\n\n if (!createData.post_id) {\n throw new Error(createData.error || '创建草稿失败: 无效响应')\n }\n\n const draftId = String(createData.post_id)\n const draftUrl = createData.url || `https://www.woshipm.com/writing?pid=${draftId}`\n\n logger.debug('Draft created:', draftId)\n\n return this.createResult(true, {\n postId: draftId,\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n /**\n * 通过 Blob 上传图片(覆盖基类方法)\n */\n async uploadImage(file: Blob, filename?: string): Promise<string> {\n return this.uploadImageBinaryInternal(file, filename || 'image.png')\n }\n\n /**\n * 通过 URL 上传图片\n */\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n try {\n // 1. 下载图片(使用 runtime.fetch 以支持跨域)\n const imageResponse = await this.runtime.fetch(src, {\n credentials: 'omit',\n })\n if (!imageResponse.ok) {\n throw new Error(`Failed to fetch image: ${imageResponse.status}`)\n }\n\n const blob = await imageResponse.blob()\n\n // 2. 上传到 woshipm\n const url = await this.uploadImageBinaryInternal(blob, this.getFilenameFromUrl(src))\n return { url }\n } catch (error) {\n logger.warn('Failed to upload image by URL:', src, error)\n return { url: src } // 失败时返回原 URL\n }\n }\n\n /**\n * 上传图片 (二进制方式) - 内部使用\n */\n private async uploadImageBinaryInternal(file: Blob, filename: string): Promise<string> {\n const formData = new FormData()\n formData.append('action', 'wpuf_insert_image')\n formData.append('name', filename)\n formData.append('files', file, filename)\n\n const headers: Record<string, string> = {\n 'Origin': 'https://www.woshipm.com',\n 'Referer': 'https://www.woshipm.com/writing',\n }\n if (this.jltoken) {\n headers['jlstar'] = `Bearer ${this.jltoken}`\n }\n\n const response = await this.runtime.fetch('https://www.woshipm.com/tensorflow/upyun/upload', {\n method: 'POST',\n credentials: 'include',\n headers,\n body: formData,\n })\n\n const data = await response.json() as {\n data?: Array<{ url?: string }>\n error?: string\n }\n\n if (data.data && data.data.length > 0 && data.data[0].url) {\n logger.debug('Uploaded image:', filename, '->', data.data[0].url)\n return data.data[0].url\n }\n\n throw new Error(data.error || 'Failed to upload image')\n }\n\n /**\n * 从 URL 提取文件名\n */\n private getFilenameFromUrl(url: string): string {\n try {\n const pathname = new URL(url).pathname\n const filename = pathname.split('/').pop() || 'image.png'\n return filename\n } catch {\n return 'image.png'\n }\n }\n}\n","/**\n * 语雀适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { createLogger } from '../../lib/logger'\n\nconst logger = createLogger('Yuque')\n\ninterface YuqueUserInfo {\n id: number\n name: string\n avatar_url: string\n}\n\ninterface YuqueBook {\n target_id: number\n user: YuqueUserInfo\n}\n\nexport class YuqueAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'yuque',\n name: '语雀',\n icon: 'https://gw.alipayobjects.com/zos/rmsportal/UTjFYEzMSYVwzxIGVhMu.png',\n homepage: 'https://www.yuque.com/dashboard',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 语雀使用 Markdown 格式 (转换为 lake) */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n // doPreFilter + processDocCode (旧版)\n removeSpecialTags: true,\n removeSpecialTagsWithParent: true,\n processCodeBlocks: true,\n }\n\n private userInfo: YuqueUserInfo | null = null\n private bookId: number | null = null\n private csrfToken: string = ''\n private currentPostId: number | null = null\n\n /** 语雀 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://www.yuque.com/api/*',\n headers: {\n 'Origin': 'https://www.yuque.com',\n 'Referer': 'https://www.yuque.com/dashboard',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n private async getCsrfToken(): Promise<string> {\n if (this.runtime.getCookie) {\n const value = await this.runtime.getCookie('.yuque.com', 'yuque_ctoken')\n if (!value) {\n throw new Error('请先登录语雀')\n }\n return value\n }\n throw new Error('请先登录语雀')\n }\n\n async checkAuth(): Promise<AuthResult> {\n try {\n this.csrfToken = await this.getCsrfToken()\n\n const response = await this.runtime.fetch(\n 'https://www.yuque.com/api/mine/common_used',\n {\n method: 'GET',\n credentials: 'include',\n headers: {\n 'x-csrf-token': this.csrfToken,\n },\n }\n )\n\n const res = await response.json() as {\n data?: {\n books?: YuqueBook[]\n }\n }\n\n logger.debug('checkAuth response:', res)\n\n if (res.data?.books && res.data.books.length > 0) {\n const firstBook = res.data.books[0]\n this.userInfo = firstBook.user\n this.bookId = firstBook.target_id\n\n return {\n isAuthenticated: true,\n userId: String(firstBook.user.id),\n username: firstBook.user.name,\n avatar: firstBook.user.avatar_url,\n }\n }\n\n return { isAuthenticated: false }\n } catch (error) {\n logger.debug('checkAuth: not logged in -', (error as Error).message)\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n logger.info('Starting publish...')\n\n if (!this.userInfo || !this.bookId) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('请先登录语雀')\n }\n }\n\n const createResponse = await this.runtime.fetch(\n 'https://www.yuque.com/api/docs',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n 'x-csrf-token': this.csrfToken,\n },\n body: JSON.stringify({\n title: article.title,\n type: 'Doc',\n format: 'lake',\n book_id: this.bookId,\n status: 0,\n }),\n }\n )\n\n const createRes = await createResponse.json() as {\n data?: { id: number }\n message?: string\n }\n\n logger.debug('Create doc response:', createRes)\n\n if (!createRes.data?.id) {\n throw new Error(createRes.message || '创建文档失败')\n }\n\n const postId = createRes.data.id\n this.currentPostId = postId\n\n // Use pre-processed markdown content directly\n let markdown = article.markdown || ''\n\n // Process images in markdown\n markdown = await this.processImages(\n markdown,\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: ['yuque.com', 'cdn.nlark.com'],\n onProgress: options?.onImageProgress,\n }\n )\n\n const convertResponse = await this.runtime.fetch(\n 'https://www.yuque.com/api/docs/convert',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n 'x-csrf-token': this.csrfToken,\n },\n body: JSON.stringify({\n from: 'markdown',\n to: 'lake',\n content: markdown,\n }),\n }\n )\n\n const convertRes = await convertResponse.json() as {\n data?: { content: string }\n }\n\n if (!convertRes.data?.content) {\n throw new Error('内容转换失败')\n }\n\n const lakeContent = convertRes.data.content\n\n const saveResponse = await this.runtime.fetch(\n `https://www.yuque.com/api/docs/${postId}/content`,\n {\n method: 'PUT',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n 'x-csrf-token': this.csrfToken,\n },\n body: JSON.stringify({\n format: 'lake',\n body_asl: lakeContent,\n body: `<div class=\"lake-content\" typography=\"traditional\">${lakeContent}</div>`,\n body_html: `<div class=\"lake-content\" typography=\"traditional\">${lakeContent}</div>`,\n draft_version: 0,\n sync_dynamic_data: false,\n save_type: 'auto',\n edit_type: 'Lake',\n }),\n }\n )\n\n const saveRes = await saveResponse.json()\n logger.debug('Save response:', saveRes)\n\n const draftUrl = `https://www.yuque.com/go/doc/${postId}/edit`\n\n return this.createResult(true, {\n postId: String(postId),\n postUrl: draftUrl,\n })\n }).catch((error) => this.createResult(false, {\n error: (error as Error).message,\n }))\n }\n\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n if (!this.currentPostId) {\n throw new Error('文档 ID 未设置')\n }\n\n const imageResponse = await fetch(src)\n if (!imageResponse.ok) {\n throw new Error('图片下载失败: ' + src)\n }\n const imageBlob = await imageResponse.blob()\n\n const formData = new FormData()\n formData.append('file', imageBlob, 'image.jpg')\n\n const uploadUrl = `https://www.yuque.com/api/upload/attach?attachable_type=Doc&attachable_id=${this.currentPostId}&type=image`\n const uploadResponse = await this.runtime.fetch(uploadUrl, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'x-csrf-token': this.csrfToken,\n },\n body: formData,\n })\n\n const res = await uploadResponse.json() as {\n data?: {\n attachment_id: string\n url: string\n }\n }\n\n logger.debug('Image upload response:', res)\n\n if (!res.data?.url) {\n throw new Error('图片上传失败')\n }\n\n return {\n url: res.data.url,\n }\n }\n}\n","/**\n * 慕课网手记适配器\n * https://www.imooc.com\n */\nimport { CodeAdapter, ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nexport class ImoocAdapter extends CodeAdapter {\n meta: PlatformMeta = {\n id: 'imooc',\n name: '慕课手记',\n icon: 'https://www.imooc.com/favicon.ico',\n homepage: 'https://www.imooc.com/article',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 慕课网使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n /** 慕课网 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://www.imooc.com/article/*',\n headers: {\n Origin: 'https://www.imooc.com',\n Referer: 'https://www.imooc.com/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n /**\n * 检查登录状态\n */\n async checkAuth(): Promise<AuthResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n const response = await this.runtime.fetch('https://www.imooc.com/u/card', {\n credentials: 'include',\n })\n let text = await response.text()\n\n // 解析 JSONP 响应\n text = text.replace('jsonpcallback(', '').replace('})', '}')\n const result = JSON.parse(text)\n\n if (result.result !== 0) {\n return { isAuthenticated: false, error: result.msg || '未登录' }\n }\n\n return {\n isAuthenticated: true,\n userId: result.data.uid,\n username: result.data.nickname,\n avatar: result.data.img,\n }\n }).catch((error) => ({ isAuthenticated: false, error: (error as Error).message }))\n }\n\n /**\n * 上传图片\n */\n async uploadImageByUrl(url: string): Promise<ImageUploadResult> {\n // 下载图片\n const imageResponse = await this.runtime.fetch(url)\n const blob = await imageResponse.blob()\n\n // 构建 FormData\n const formData = new FormData()\n const filename = `${Date.now()}.jpg`\n const file = new File([blob], filename, { type: blob.type || 'image/jpeg' })\n\n formData.append('photo', file, filename)\n formData.append('type', file.type)\n formData.append('id', 'WU_FILE_0')\n formData.append('name', filename)\n formData.append('lastModifiedDate', new Date().toString())\n formData.append('size', String(file.size))\n\n const response = await this.runtime.fetch(\n 'https://www.imooc.com/article/ajaxuploadimg',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await response.json()\n\n if (res.result !== 0) {\n throw new Error(res.msg || '图片上传失败')\n }\n\n // 处理协议相对 URL\n let imgUrl = res.data.imgpath\n if (imgUrl.startsWith('//')) {\n imgUrl = 'https:' + imgUrl\n }\n\n return { url: imgUrl }\n }\n\n /**\n * 发布文章\n */\n async publish(article: Article): Promise<SyncResult> {\n const now = Date.now()\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n // 优先使用 markdown,处理图片\n let content = article.markdown || article.html || ''\n content = await this.processImages(content, (src) => this.uploadImageByUrl(src))\n\n const response = await this.runtime.fetch('https://www.imooc.com/article/savedraft', {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n editor: '0',\n draft_id: '0',\n title: article.title,\n content: content,\n }),\n })\n\n const res = await response.json()\n\n if (!res.data) {\n throw new Error('发布失败')\n }\n\n return {\n platform: this.meta.id,\n success: true,\n postId: res.data,\n postUrl: `https://www.imooc.com/article/draft/id/${res.data}`,\n timestamp: now,\n }\n }).catch((error) => ({\n platform: this.meta.id,\n success: false,\n error: (error as Error).message,\n timestamp: now,\n }))\n }\n}\n","/**\n * 开源中国适配器\n * https://my.oschina.net\n */\nimport { CodeAdapter, ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nexport class OschinaAdapter extends CodeAdapter {\n meta: PlatformMeta = {\n id: 'oschina',\n name: '开源中国',\n icon: 'https://www.oschina.net/favicon.ico',\n homepage: 'https://my.oschina.net',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 开源中国使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n private userId: string | null = null\n\n /** 开源中国 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://apiv1.oschina.net/oschinapi/*',\n headers: {\n Origin: 'https://my.oschina.net',\n Referer: 'https://my.oschina.net/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n /**\n * 检查登录状态\n */\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch('https://apiv1.oschina.net/oschinapi/user/myDetails', {\n credentials: 'include',\n })\n const data = await response.json() as {\n success: boolean\n result?: {\n userId: number\n userVo?: {\n name: string\n portraitUrl: string\n }\n }\n }\n\n if (!data.success || !data.result?.userId) {\n return { isAuthenticated: false, error: '未登录' }\n }\n\n this.userId = String(data.result.userId)\n\n return {\n isAuthenticated: true,\n userId: this.userId,\n username: data.result.userVo?.name || this.userId,\n avatar: data.result.userVo?.portraitUrl,\n }\n } catch (error) {\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n /**\n * 上传图片\n */\n async uploadImageByUrl(url: string): Promise<ImageUploadResult> {\n if (!this.userId) {\n await this.checkAuth()\n }\n\n // 下载图片\n const imageResponse = await this.runtime.fetch(url)\n const blob = await imageResponse.blob()\n const filename = this.getFilenameFromUrl(url) || 'image'\n\n // 构建 FormData\n const formData = new FormData()\n formData.append('file', blob, filename)\n\n const response = await this.runtime.fetch(\n 'https://apiv1.oschina.net/oschinapi/ai/creation/project/uploadDetail',\n {\n method: 'POST',\n credentials: 'include',\n body: formData,\n }\n )\n\n const res = await response.json() as {\n success?: boolean\n result?: string\n message?: string\n }\n\n if (!res.success || !res.result) {\n throw new Error(res.message || '图片上传失败')\n }\n\n return { url: res.result }\n }\n\n private getFilenameFromUrl(url: string): string | null {\n try {\n const pathname = new URL(url).pathname\n const name = pathname.split('/').pop()\n return name && name.trim() ? name : null\n } catch {\n return null\n }\n }\n\n /**\n * 发布文章\n */\n async publish(article: Article): Promise<SyncResult> {\n const now = Date.now()\n\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n // 确保已获取用户 ID\n if (!this.userId) {\n const auth = await this.checkAuth()\n if (!auth.isAuthenticated) {\n throw new Error('未登录')\n }\n }\n\n const rawMarkdown = article.markdown || ''\n const rawHtml = article.html || ''\n const useMarkdown = rawMarkdown.trim().length > 0\n\n let content = useMarkdown ? rawMarkdown : rawHtml\n content = await this.processImages(content, (src) => this.uploadImageByUrl(src))\n\n const response = await this.runtime.fetch(\n 'https://apiv1.oschina.net/oschinapi/api/draft/save_draft',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n title: article.title,\n user: Number(this.userId),\n content,\n contentType: useMarkdown ? 1 : 2, // 1=markdown, 2=html\n catalog: 0,\n originUrl: '',\n privacy: true,\n disableComment: false,\n }),\n }\n )\n\n const res = await response.json() as {\n success?: boolean\n message?: string\n result?: { id?: number }\n }\n\n if (!res.success || !res.result?.id) {\n throw new Error(res.message || '发布失败')\n }\n\n const draftId = String(res.result.id)\n\n return {\n platform: this.meta.id,\n success: true,\n postId: draftId,\n postUrl: `https://my.oschina.net/u/${this.userId}/blog/write/draft/${draftId}`,\n timestamp: now,\n }\n }).catch((error) => ({\n platform: this.meta.id,\n success: false,\n error: (error as Error).message,\n timestamp: now,\n }))\n }\n}\n","/**\n * 思否 (Segmentfault) 适配器\n * https://segmentfault.com\n */\nimport { CodeAdapter, ImageUploadResult } from '../code-adapter'\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nexport class SegmentfaultAdapter extends CodeAdapter {\n meta: PlatformMeta = {\n id: 'segmentfault',\n name: '思否',\n icon: 'https://imgcache.iyiou.com/Company/2016-05-11/cf-segmentfault.jpg',\n homepage: 'https://segmentfault.com/user/draft',\n capabilities: ['article', 'draft', 'image_upload'],\n }\n\n /** 预处理配置: 思否使用 Markdown 格式 */\n readonly preprocessConfig = {\n outputFormat: 'markdown' as const,\n }\n\n private sessionToken: string | null = null\n\n /** 思否 API 需要的 Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: '*://segmentfault.com/gateway/*',\n headers: {\n Origin: 'https://segmentfault.com',\n Referer: 'https://segmentfault.com/',\n },\n resourceTypes: ['xmlhttprequest'],\n },\n ]\n\n /**\n * 检查登录状态\n */\n async checkAuth(): Promise<AuthResult> {\n try {\n const response = await this.runtime.fetch('https://segmentfault.com/user/settings', {\n credentials: 'include',\n })\n const html = await response.text()\n\n // 匹配用户链接 href=\"/u/username\"\n const userLinkMatch = html.match(/href=\"\\/u\\/([^\"]+)\"/)\n if (!userLinkMatch) {\n return { isAuthenticated: false, error: '未登录' }\n }\n\n const uid = userLinkMatch[1]\n\n // 匹配头像 URL (avatar-static.segmentfault.com)\n const avatarMatch = html.match(/src=\"(https:\\/\\/avatar-static\\.segmentfault\\.com\\/[^\"]+)\"/)\n const avatar = avatarMatch ? avatarMatch[1] : undefined\n\n return {\n isAuthenticated: true,\n userId: uid,\n username: uid,\n avatar: avatar,\n }\n } catch (error) {\n return { isAuthenticated: false, error: (error as Error).message }\n }\n }\n\n /**\n * 获取 session token\n */\n private async getSessionToken(): Promise<string> {\n const response = await this.runtime.fetch('https://segmentfault.com/write', {\n credentials: 'include',\n })\n const html = await response.text()\n\n // 新版 token 格式: serverData\":{\"Token\":\"xxx\"\n const tokenMatch = html.match(/serverData\":\\s*\\{\\s*\"Token\"\\s*:\\s*\"([^\"]+)\"/)\n if (tokenMatch) {\n return tokenMatch[1]\n }\n\n // 兼容旧版格式\n const markStr = 'window.g_initialProps = '\n const authIndex = html.indexOf(markStr)\n if (authIndex === -1) {\n throw new Error('获取 session token 失败')\n }\n\n const endIndex = html.indexOf(';\\n\\t</script>', authIndex)\n if (endIndex === -1) {\n throw new Error('解析 session token 失败')\n }\n\n const configStr = html.substring(authIndex + markStr.length, endIndex)\n\n try {\n const config = JSON.parse(configStr)\n const token = config?.global?.sessionInfo?.key\n if (!token) {\n throw new Error('session token 为空')\n }\n return token\n } catch (e) {\n throw new Error('解析 session token 失败: ' + (e as Error).message)\n }\n }\n\n /**\n * 上传图片\n */\n async uploadImageByUrl(url: string): Promise<ImageUploadResult> {\n if (!this.sessionToken) {\n throw new Error('未获取 token')\n }\n\n // 下载图片\n const imageResponse = await this.runtime.fetch(url)\n const blob = await imageResponse.blob()\n\n // 构建 FormData\n const formData = new FormData()\n formData.append('image', blob)\n\n const response = await this.runtime.fetch(\n 'https://segmentfault.com/gateway/image',\n {\n method: 'POST',\n credentials: 'include',\n headers: {\n token: this.sessionToken,\n },\n body: formData,\n }\n )\n\n // 处理异常响应\n const text = await response.text()\n if (text === 'Unauthorized' || text.includes('禁言') || text.includes('锁定')) {\n throw new Error(text === 'Unauthorized' ? '未授权' : text)\n }\n\n let res\n try {\n res = JSON.parse(text)\n } catch {\n throw new Error('图片上传失败: ' + text)\n }\n\n // 新版返回格式: { url: \"/img/xxx\", result: \"https://...\" }\n // 旧版返回格式: [0, url, id] 或 [1, error_message]\n const imageUrl = res.result || (Array.isArray(res) ? (res[0] === 1 ? null : res[1] || `https://image-static.segmentfault.com/${res[2]}`) : null)\n if (!imageUrl) {\n throw new Error(Array.isArray(res) ? (res[1] || '图片上传失败') : '图片上传失败')\n }\n return { url: imageUrl }\n }\n\n /**\n * 发布文章\n */\n async publish(article: Article): Promise<SyncResult> {\n const now = Date.now()\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n // 获取 session token\n this.sessionToken = await this.getSessionToken()\n\n // 优先使用 markdown,处理图片\n let content = article.markdown || article.html || ''\n content = await this.processImages(content, (src) => this.uploadImageByUrl(src))\n\n const postData = {\n title: article.title,\n tags: [],\n text: content,\n object_id: '',\n type: 'article',\n }\n\n const response = await this.runtime.fetch('https://segmentfault.com/gateway/draft', {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n token: this.sessionToken,\n accept: '*/*',\n },\n body: JSON.stringify(postData),\n })\n\n // 处理异常响应\n const text = await response.text()\n if (text === 'Unauthorized' || text.includes('禁言') || text.includes('锁定')) {\n throw new Error(text === 'Unauthorized' ? '未授权' : text)\n }\n\n let res\n try {\n res = JSON.parse(text)\n } catch {\n throw new Error('发布失败: ' + text)\n }\n\n // 处理数组格式响应 [1, \"error_message\"]\n if (Array.isArray(res)) {\n if (res[0] === 1) {\n throw new Error(res[1] || '发布失败')\n }\n // [0, data] 成功格式\n const data = res[1]\n if (data?.id) {\n return {\n platform: this.meta.id,\n success: true,\n postId: data.id,\n postUrl: `https://segmentfault.com/write?draftId=${data.id}`,\n timestamp: now,\n }\n }\n }\n\n if (!res.id) {\n // 尝试多种错误字段\n const errorMsg = res.message || res.msg || res.error || res.errMsg || JSON.stringify(res)\n throw new Error(errorMsg)\n }\n\n return {\n platform: this.meta.id,\n success: true,\n postId: res.id,\n postUrl: `https://segmentfault.com/write?draftId=${res.id}`,\n timestamp: now,\n }\n }).catch((error) => ({\n platform: this.meta.id,\n success: false,\n error: (error as Error).message,\n timestamp: now,\n }))\n }\n}\n","/**\n * Markdown 压缩包下载适配器\n *\n * 将文章导出为 Markdown + 图片的 ZIP 压缩包:\n * - article.md: Markdown 文件\n * - images/: 图片目录,所有图片使用相对路径引用\n *\n * 使用 chrome.downloads API 直接触发下载,避免大文件传递问题\n */\nimport type { Article, AuthResult, SyncResult, PlatformMeta } from '../../types'\nimport type { PublishOptions } from '../types'\nimport { CodeAdapter } from '../code-adapter'\nimport { htmlToMarkdown } from '../../lib/turndown'\nimport { createLogger } from '../../lib/logger'\nimport { parseMarkdownImages } from '../../lib/markdown-images'\nimport JSZip from 'jszip'\n\nconst logger = createLogger('ZipDownload')\n\nexport class ZipDownloadAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: 'zip-download',\n name: 'Markdown 压缩包',\n icon: 'https://cdn-icons-png.flaticon.com/512/337/337946.png',\n homepage: '',\n capabilities: ['article'],\n }\n\n /**\n * 本地导出不需要认证\n */\n async checkAuth(): Promise<AuthResult> {\n return {\n isAuthenticated: true,\n username: '本地下载',\n }\n }\n\n /**\n * 导出为 ZIP 压缩包\n */\n async publish(article: Article, options?: PublishOptions): Promise<SyncResult> {\n try {\n const zip = new JSZip()\n const imgFolder = zip.folder('images')!\n\n // 获取 Markdown 内容\n let markdown = article.markdown || htmlToMarkdown(article.html || '')\n\n if (!markdown.trim()) {\n return this.createResult(false, {\n error: '文章内容为空',\n })\n }\n\n // 添加标题\n const title = article.title || '未命名文章'\n if (!markdown.startsWith('# ')) {\n markdown = `# ${title}\\n\\n${markdown}`\n }\n\n // 处理图片:下载并替换为相对路径\n const { processedMarkdown, imageCount } = await this.processImagesForZip(\n markdown,\n imgFolder,\n options?.onImageProgress\n )\n\n // 写入 Markdown 文件\n zip.file('article.md', processedMarkdown)\n\n // 生成 ZIP Blob\n const blob = await zip.generateAsync({\n type: 'blob',\n compression: 'DEFLATE',\n compressionOptions: { level: 6 },\n })\n\n // 使用 runtime.downloads 下载\n const filename = this.sanitizeFilename(title) + '.zip'\n\n if (!this.runtime.downloads) {\n return this.createResult(false, {\n error: '当前环境不支持下载功能',\n })\n }\n\n const downloadId = await this.runtime.downloads.download(blob, filename, true)\n logger.info(`Download started: ${filename}, id: ${downloadId}`)\n\n return this.createResult(true, {\n postId: String(downloadId),\n postUrl: '', // 本地下载,无链接\n message: `已下载 ${filename}(${imageCount} 张图片)`,\n })\n } catch (error) {\n logger.error('ZIP download failed:', error)\n return this.createResult(false, {\n error: (error as Error).message,\n })\n }\n }\n\n /**\n * 处理 Markdown 中的图片:下载并添加到 ZIP\n */\n private async processImagesForZip(\n markdown: string,\n imgFolder: JSZip,\n onProgress?: (current: number, total: number) => void\n ): Promise<{ processedMarkdown: string; imageCount: number }> {\n const matches = parseMarkdownImages(markdown)\n\n if (matches.length === 0) {\n return { processedMarkdown: markdown, imageCount: 0 }\n }\n\n logger.info(`Found ${matches.length} images to process`)\n\n // 生成时间戳前缀(精确到秒)\n const timestamp = Math.floor(Date.now() / 1000)\n\n let processedMarkdown = markdown\n let imageIndex = 0\n let completed = 0\n const urlToFilename = new Map<string, string>() // 去重:相同 URL 使用相同文件名\n\n const uniqueUrls: string[] = []\n for (const { src } of matches) {\n if (src.startsWith('data:')) {\n logger.debug('Skipping data URI image')\n continue\n }\n if (!urlToFilename.has(src)) {\n urlToFilename.set(src, '')\n uniqueUrls.push(src)\n }\n }\n\n if (uniqueUrls.length === 0) {\n return { processedMarkdown: markdown, imageCount: 0 }\n }\n\n const downloadOne = async (src: string) => {\n try {\n const response = await this.runtime.fetch(src, {\n credentials: 'omit',\n })\n\n if (!response.ok) {\n logger.warn(`Failed to download image: ${src}, status: ${response.status}`)\n return\n }\n\n const blob = await response.blob()\n const ext = this.getImageExtension(src, blob.type)\n imageIndex++\n const filename = `image_${timestamp}_${String(imageIndex).padStart(3, '0')}.${ext}`\n\n imgFolder.file(filename, blob)\n urlToFilename.set(src, filename)\n\n logger.debug(`Downloaded image: ${filename}`)\n } catch (error) {\n logger.warn(`Failed to download image: ${src}`, error)\n } finally {\n completed++\n onProgress?.(completed, uniqueUrls.length)\n }\n }\n\n const runPool = async (limit: number) => {\n let cursor = 0\n const workers = Array.from({ length: Math.min(limit, uniqueUrls.length) }, async () => {\n while (cursor < uniqueUrls.length) {\n const current = cursor\n cursor++\n await downloadOne(uniqueUrls[current])\n }\n })\n await Promise.all(workers)\n }\n\n await runPool(4)\n\n for (const { full, alt, src } of matches) {\n const filename = urlToFilename.get(src)\n if (filename) {\n processedMarkdown = processedMarkdown.replace(full, ``)\n }\n }\n\n return {\n processedMarkdown,\n imageCount: Array.from(urlToFilename.values()).filter(Boolean).length,\n }\n }\n\n /**\n * 获取图片扩展名\n */\n private getImageExtension(url: string, mimeType: string): string {\n // 优先从 MIME 类型获取\n const mimeMap: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/svg+xml': 'svg',\n 'image/bmp': 'bmp',\n 'image/x-icon': 'ico',\n }\n\n if (mimeType && mimeMap[mimeType]) {\n return mimeMap[mimeType]\n }\n\n // 从 URL 提取\n const urlMatch = url.match(/\\.(png|jpe?g|gif|webp|svg|bmp|ico)(?:\\?|$)/i)\n if (urlMatch) {\n return urlMatch[1].toLowerCase().replace('jpeg', 'jpg')\n }\n\n // 默认 png\n return 'png'\n }\n\n /**\n * 清理文件名,移除非法字符\n */\n private sanitizeFilename(name: string): string {\n return name\n .replace(/[<>:\"/\\\\|?*]/g, '_') // Windows 非法字符\n .replace(/[\\x00-\\x1f]/g, '') // 控制字符\n .replace(/\\.+$/, '') // 末尾的点\n .trim()\n || 'article'\n }\n}\n","/**\n * 东方财富适配器\n */\nimport { CodeAdapter, type ImageUploadResult } from \"../code-adapter\";\nimport type {\n Article,\n AuthResult,\n SyncResult,\n PlatformMeta,\n} from \"../../types\";\nimport type { PublishOptions } from \"../types\";\nimport { createLogger } from \"../../lib/logger\";\n\nconst logger = createLogger(\"Eastmoney\");\n\ninterface UploadResponse {\n code: number;\n message: string;\n data?: {\n url: string;\n id: string;\n };\n}\n\ninterface DraftApiResponse {\n RRquestSuccess: boolean;\n RCode: number;\n RMsg?: string;\n RData: string;\n}\n\ninterface DraftResult {\n error_code?: number;\n draft_id?: string;\n me?: string;\n}\n\nexport class EastmoneyAdapter extends CodeAdapter {\n readonly meta: PlatformMeta = {\n id: \"eastmoney\",\n name: \"东方财富\",\n icon: \"https://mp.eastmoney.com/collect/pc_article/favicon.ico\",\n homepage: \"https://mp.eastmoney.com\",\n capabilities: [\"article\", \"draft\", \"image_upload\", \"cover\"],\n };\n\n /** 预处理配置 */\n readonly preprocessConfig = {\n outputFormat: \"html\" as const,\n removeComments: true,\n removeSpecialTags: true,\n processCodeBlocks: true,\n convertSectionToDiv: true,\n removeEmptyLines: true,\n removeEmptyDivs: true,\n removeNestedEmptyContainers: true,\n unwrapSingleChildContainers: true,\n unwrapNestedFigures: true,\n removeTrailingBr: true,\n removeDataAttributes: true,\n removeSrcset: true,\n removeSizes: true,\n compactHtml: true,\n };\n\n private ctoken: string = \"\";\n private utoken: string = \"\";\n private deviceId: string = \"\";\n\n /** 获取或生成持久化 deviceId(32位大写 hex) */\n private async getDeviceId(): Promise<string> {\n if (this.deviceId) return this.deviceId;\n const stored = await this.runtime.storage.get<string>(\"eastmoney_deviceId\");\n if (stored) {\n this.deviceId = stored;\n return this.deviceId;\n }\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n this.deviceId = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\").toUpperCase())\n .join(\"\");\n await this.runtime.storage.set(\"eastmoney_deviceId\", this.deviceId);\n return this.deviceId;\n }\n\n /** API Header 规则 */\n private readonly HEADER_RULES = [\n {\n urlFilter: \"*://mp.eastmoney.com/*\",\n headers: {\n Origin: \"https://mp.eastmoney.com\",\n HOST: \"emfront.eastmoney.com\",\n },\n resourceTypes: [\"xmlhttprequest\"],\n },\n ];\n\n async checkAuth(): Promise<AuthResult> {\n try {\n await this.fetchToken();\n\n const response = await this.runtime.fetch(\n `https://caifuhaoapi.eastmoney.com/api/v2/getauthorinfo?platform=&ctoken=${this.ctoken}&utoken=${this.utoken}`,\n {\n method: \"GET\",\n credentials: \"include\",\n headers: { \"x-requested-with\": \"fetch\" },\n },\n );\n\n const data = (await response.json()) as {\n Success: number;\n Result?: {\n accountId?: string;\n accountName?: string;\n portrait?: string;\n };\n };\n\n if (data.Success === 1 && data.Result?.accountId) {\n return {\n isAuthenticated: true,\n userId: data.Result.accountId,\n username: data.Result.accountName,\n avatar: data.Result.portrait,\n };\n }\n\n return { isAuthenticated: false };\n } catch (error) {\n logger.debug(\"checkAuth: not logged in -\", error);\n return { isAuthenticated: false, error: (error as Error).message };\n }\n }\n\n /** 从 cookie 读取 token */\n private async fetchToken(): Promise<void> {\n if (!this.runtime.getCookie) {\n throw new Error(\"Cookie API 不可用,请先登录东方财富\");\n }\n\n const ctoken = await this.runtime.getCookie(\".eastmoney.com\", \"ct\");\n const utoken = await this.runtime.getCookie(\".eastmoney.com\", \"ut\");\n\n if (!ctoken || !utoken) {\n throw new Error(\"未检测到登录信息,请先登录东方财富\");\n }\n\n this.ctoken = ctoken;\n this.utoken = utoken;\n }\n\n async publish(\n article: Article,\n options?: PublishOptions,\n ): Promise<SyncResult> {\n return this.withHeaderRules(this.HEADER_RULES, async () => {\n await this.fetchToken();\n logger.info(\"Starting publish to eastmoney...\");\n\n // 1. 创建空草稿,获取 draft_id\n const draftId = await this.createDraft(article.title);\n logger.debug(\"Draft created:\", draftId);\n\n // 2. 处理图片(基于预处理后的 HTML)\n const content = await this.processImages(\n article.html || \"\",\n (src) => this.uploadImageByUrl(src),\n {\n skipPatterns: [\"gbres.dfcfw.com\"],\n onProgress: options?.onImageProgress,\n },\n );\n\n // 3. 更新草稿内容\n await this.updateDraft(draftId, article.title, content);\n logger.debug(\"Draft updated\");\n\n const draftUrl = `https://mp.eastmoney.com/collect/pc_article/index.html#/?id=${draftId}`;\n\n return this.createResult(true, {\n postId: draftId,\n postUrl: draftUrl,\n });\n }).catch((error) =>\n this.createResult(false, {\n error: (error as Error).message,\n }),\n );\n }\n\n /** 构造 API 参数 */\n private async buildParm(params: {\n draftid?: string;\n title: string;\n text: string;\n }): Promise<object[]> {\n const deviceid = await this.getDeviceId();\n return [\n { ip: \"$IP$\" },\n { deviceid },\n { version: \"100\" },\n { plat: \"web\" },\n { product: \"CFH\" },\n { ctoken: this.ctoken },\n { utoken: this.utoken },\n { draftid: params.draftid ?? \"\" },\n { drafttype: \"0\" },\n { type: \"0\" },\n { title: encodeURIComponent(params.title) },\n { text: encodeURIComponent(params.text) },\n { columns: \"2\" },\n { cover: \"\" },\n { issimplevideo: \"0\" },\n { videos: \"\" },\n { vods: \"\" },\n { isoriginal: \"0\" },\n { tgProduct: \"\" },\n { spcolumns: \"\" },\n { textsource: \"0\" },\n { replyauthority: \"\" },\n { modules: encodeURIComponent(\"[]\") },\n ];\n }\n\n /** 调用草稿 API */\n private async callDraftApi(parm: object[], draftId?: string): Promise<DraftResult> {\n const pageUrl = draftId\n ? `https://mp.eastmoney.com/collect/pc_article/index.html#/?id=${draftId}`\n : \"https://mp.eastmoney.com/collect/pc_article/index.html#/\";\n\n const body = JSON.stringify({\n pageUrl,\n path: \"draft/api/Article/SaveDraft\",\n parm: JSON.stringify(parm),\n });\n\n const response = await this.runtime.fetch(\n \"https://emfront.eastmoney.com/apifront/Tran/GetData?platform=\",\n {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n },\n );\n\n const responseText = await response.text();\n logger.debug(\n \"Draft API response:\",\n response.status,\n responseText.substring(0, 200),\n );\n\n if (!response.ok) {\n throw new Error(`草稿 API 请求失败: ${response.status}`);\n }\n\n let rawData: DraftApiResponse;\n try {\n rawData = JSON.parse(responseText);\n } catch {\n throw new Error(\"草稿 API 响应不是有效 JSON\");\n }\n\n if (!rawData.RRquestSuccess || rawData.RCode !== 200) {\n throw new Error(`草稿 API 错误: ${rawData.RMsg || \"未知错误\"}`);\n }\n\n let innerData: DraftResult;\n try {\n innerData = JSON.parse(rawData.RData);\n } catch {\n throw new Error(\"无法解析草稿响应数据\");\n }\n\n if (innerData.error_code !== 0) {\n throw new Error(`草稿业务错误: ${innerData.me || \"未知错误\"}`);\n }\n\n return innerData;\n }\n\n private async createDraft(title: string): Promise<string> {\n const parm = await this.buildParm({\n title,\n text: '<div class=\"xeditor_content cfh_web\"></div>',\n });\n const result = await this.callDraftApi(parm);\n if (!result.draft_id) {\n throw new Error(\"创建草稿失败: 响应缺少 draft_id\");\n }\n return result.draft_id;\n }\n\n private async updateDraft(\n draftId: string,\n title: string,\n content: string,\n ): Promise<void> {\n const parm = await this.buildParm({\n draftid: draftId,\n title,\n text: `<div class=\"xeditor_content cfh_web\">${content}</div>`,\n });\n await this.callDraftApi(parm, draftId);\n }\n\n /** URL 上传图片 */\n protected async uploadImageByUrl(src: string): Promise<ImageUploadResult> {\n // data URI 使用二进制上传\n if (src.startsWith(\"data:\")) {\n logger.debug(\"Detected data URI, using binary upload\");\n const blob = await this.dataUriToBlob(src);\n return this.uploadImageBlob(blob);\n }\n\n // 远程 URL 使用链接上传接口\n const response = await this.runtime.fetch(\n \"https://gbapi.eastmoney.com/iimage/image/byLink?platform=\",\n {\n method: \"PUT\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n noinlist: \"1\",\n linkUrl: src,\n ctoken: this.ctoken,\n utoken: this.utoken,\n }),\n },\n );\n\n const res = (await response.json()) as UploadResponse;\n if (res.code === 200 && res.data?.url) {\n return { url: res.data.url };\n }\n throw new Error(\n `图片上传失败: ${res.message || \"未知错误\"} (code: ${res.code})`,\n );\n }\n\n /** 上传图片 Blob */\n private async uploadImageBlob(file: Blob): Promise<ImageUploadResult> {\n const ext = file.type.split(\"/\")[1] || \"png\";\n const filename = `${Date.now()}.${ext}`;\n\n const formData = new FormData();\n formData.append(\"file\", file, filename);\n formData.append(\"noinlist\", \"1\");\n formData.append(\"utoken\", this.utoken);\n formData.append(\"ctoken\", this.ctoken);\n\n const response = await this.runtime.fetch(\n \"https://gbapi.eastmoney.com/iimage/image?platform=\",\n {\n method: \"POST\",\n credentials: \"include\",\n body: formData,\n },\n );\n\n const res = (await response.json()) as UploadResponse;\n if (res.code === 200 && res.data?.url) {\n return { url: res.data.url };\n }\n throw new Error(\n `图片上传失败: ${res.message || \"未知错误\"} (code: ${res.code})`,\n );\n }\n}\n"],"mappings":"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","names":["logger","logger","logger","logger","logger"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -40,6 +40,6 @@ declare function publish(request: PublishRequest): Promise<PublishResult>;
|
|
|
40
40
|
declare function checkAuth(request: CheckAuthRequest): Promise<AuthResult>;
|
|
41
41
|
declare function getStatus(request: GetStatusRequest): Promise<PublishStatusResult>;
|
|
42
42
|
|
|
43
|
-
declare const VERSION = "1.0.
|
|
43
|
+
declare const VERSION = "1.0.6";
|
|
44
44
|
|
|
45
45
|
export { Article, AuthResult, type CheckAuthRequest, type GetStatusRequest, type PublishArticleInput, PublishEventHandler, type PublishRequest, type PublishRequestOptions, PublishResult, PublishStatusRef, PublishStatusResult, RuntimeInterface, VERSION, checkAuth, getStatus, publish };
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/publish.ts","../src/index.ts"],"sourcesContent":["import type {\n Article,\n AuthResult,\n PublishEventHandler,\n PublishResult,\n PublishStatusRef,\n PublishStatusResult,\n SyncResult,\n} from './types'\nimport type { RuntimeInterface } from './runtime'\nimport type { ImageProgressCallback, PlatformAdapter } from './adapters/types'\nimport { BaijiahaoAdapter } from './adapters/platforms/baijiahao'\nimport { BilibiliAdapter } from './adapters/platforms/bilibili'\nimport { CnblogsAdapter } from './adapters/platforms/cnblogs'\nimport { CSDNAdapter } from './adapters/platforms/csdn'\nimport { Cto51Adapter } from './adapters/platforms/cto51'\nimport { DouyinAdapter } from './adapters/platforms/douyin'\nimport { JuejinAdapter } from './adapters/platforms/juejin'\nimport { NeteaseAdapter } from './adapters/platforms/netease'\nimport { SohuAdapter } from './adapters/platforms/sohu'\nimport { TencentAdapter } from './adapters/platforms/tencent'\nimport { ToutiaoAdapter } from './adapters/platforms/toutiao'\nimport { WeixinAdapter } from './adapters/platforms/weixin'\n\ntype AdapterConstructor = new () => PlatformAdapter\n\nconst ADAPTERS: Record<string, AdapterConstructor> = {\n baijiahao: BaijiahaoAdapter,\n bilibili: BilibiliAdapter,\n cnblogs: CnblogsAdapter,\n csdn: CSDNAdapter,\n '51cto': Cto51Adapter,\n douyin: DouyinAdapter,\n juejin: JuejinAdapter,\n netease: NeteaseAdapter,\n sohu: SohuAdapter,\n tencent: TencentAdapter,\n toutiao: ToutiaoAdapter,\n weixin: WeixinAdapter,\n}\n\nexport interface PublishArticleInput {\n title: string\n description?: string\n body?: string\n content?: string\n markdown?: string\n html?: string\n cover?: string\n tags?: string[]\n category?: string\n images?: Article['images']\n video?: Article['video']\n source?: Article['source']\n}\n\nexport interface PublishRequestOptions {\n onImageProgress?: ImageProgressCallback\n}\n\nexport interface PublishRequest {\n platform: string\n article: Article | PublishArticleInput\n runtime: RuntimeInterface\n onEvent?: PublishEventHandler\n options?: PublishRequestOptions\n}\n\nexport interface GetStatusRequest {\n platform: string\n runtime: RuntimeInterface\n statusRef: PublishStatusRef\n}\n\nexport interface CheckAuthRequest {\n platform: string\n runtime: RuntimeInterface\n}\n\nexport function getSupportedPlatforms(): string[] {\n return Object.keys(ADAPTERS)\n}\n\nexport function createAdapter(platform: string): PlatformAdapter | null {\n const Adapter = ADAPTERS[platform]\n return Adapter ? new Adapter() : null\n}\n\nexport async function publish(request: PublishRequest): Promise<PublishResult> {\n const adapter = createAdapter(request.platform)\n if (!adapter) {\n return {\n platform: request.platform,\n state: 'failed',\n error: { message: 'Platform not found', code: 'unsupported' },\n timestamp: Date.now(),\n }\n }\n\n try {\n await adapter.init(request.runtime)\n const result = await adapter.publish(normalizeArticle(request.article), {\n ...request.options,\n onEvent: request.onEvent,\n })\n return toPublishResult(request.platform, result)\n } catch (error) {\n return failedResult(request.platform, error)\n }\n}\n\nexport async function checkAuth(request: CheckAuthRequest): Promise<AuthResult> {\n const adapter = createAdapter(request.platform)\n if (!adapter) {\n return { isAuthenticated: false, error: 'Platform not found' }\n }\n\n try {\n await adapter.init(request.runtime)\n const auth = await adapter.checkAuth()\n if (!auth.isAuthenticated && !auth.error) {\n return { ...auth, error: 'Not authenticated' }\n }\n return auth\n } catch (error) {\n return { isAuthenticated: false, error: error instanceof Error ? error.message : String(error) }\n }\n}\n\nexport async function getStatus(request: GetStatusRequest): Promise<PublishStatusResult> {\n const platform = request.platform\n const adapter = createAdapter(platform)\n if (!adapter) {\n return {\n platform,\n state: 'unknown',\n message: 'Platform not found',\n checkedAt: Date.now(),\n }\n }\n\n if (request.statusRef.platform !== platform) {\n return {\n platform,\n state: 'unknown',\n message: `statusRef belongs to ${request.statusRef.platform}`,\n checkedAt: Date.now(),\n }\n }\n\n try {\n await adapter.init(request.runtime)\n if (!adapter.getStatus) {\n return {\n platform,\n state: 'unknown',\n message: 'Adapter status query is not implemented',\n checkedAt: Date.now(),\n }\n }\n return await adapter.getStatus(request.statusRef)\n } catch (error) {\n return {\n platform,\n state: 'unknown',\n message: error instanceof Error ? error.message : String(error),\n checkedAt: Date.now(),\n }\n }\n}\n\nfunction normalizeArticle(article: Article | PublishArticleInput): Article {\n const input = article as PublishArticleInput & Partial<Article>\n const markdown = input.markdown ?? input.body ?? input.content ?? ''\n\n return {\n title: input.title,\n markdown,\n html: input.html,\n summary: input.summary ?? input.description,\n cover: input.cover,\n tags: input.tags,\n category: input.category,\n images: input.images,\n video: input.video,\n source: input.source,\n }\n}\n\nfunction toPublishResult(platform: string, result: SyncResult): PublishResult {\n if (!result.success) {\n return {\n platform,\n state: 'failed',\n error: { message: result.error || result.message || 'Publish failed' },\n timestamp: result.timestamp || Date.now(),\n }\n }\n\n const statusRef = createStatusRef(platform, result)\n if (!statusRef.id && !statusRef.url) {\n return {\n platform,\n state: 'failed',\n error: { message: 'Publish succeeded but adapter did not return a statusRef source', code: 'missing_status_ref' },\n timestamp: result.timestamp || Date.now(),\n }\n }\n\n return {\n platform,\n state: 'succeeded',\n statusRef,\n postId: result.postId,\n postUrl: result.postUrl,\n timestamp: result.timestamp || Date.now(),\n }\n}\n\nfunction createStatusRef(platform: string, result: SyncResult): PublishStatusRef {\n const kind = typeof result.statusData?.kind === 'string'\n ? result.statusData.kind\n : getStatusKind(platform)\n\n return {\n platform,\n kind,\n id: result.postId,\n url: result.postUrl,\n data: result.statusData,\n }\n}\n\nfunction getStatusKind(platform: string): string {\n const kinds: Record<string, string> = {\n bilibili: 'opus',\n cnblogs: 'article',\n douyin: 'note',\n weixin: 'appmsg',\n }\n return kinds[platform] || 'article'\n}\n\nfunction failedResult(platform: string, error: unknown): PublishResult {\n return {\n platform,\n state: 'failed',\n error: { message: error instanceof Error ? error.message : String(error) },\n timestamp: Date.now(),\n }\n}\n","export { checkAuth, getStatus, publish } from './publish'\nexport { createExtensionRuntime } from './runtime'\nexport type {\n CheckAuthRequest,\n GetStatusRequest,\n PublishArticleInput,\n PublishRequest,\n PublishRequestOptions,\n} from './publish'\nexport type {\n Article,\n AuthResult,\n PublishError,\n PublishEvent,\n PublishEventHandler,\n PublishResult,\n PublishState,\n PublishStatusRef,\n PublishStatusResult,\n PublishStatusState,\n} from './types'\nexport type {\n RuntimeConfig,\n RuntimeInterface,\n} from './runtime/interface'\n\n// Version\nexport const VERSION = '1.0.5'\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/publish.ts","../src/index.ts"],"sourcesContent":["import type {\n Article,\n AuthResult,\n PublishEventHandler,\n PublishResult,\n PublishStatusRef,\n PublishStatusResult,\n SyncResult,\n} from './types'\nimport type { RuntimeInterface } from './runtime'\nimport type { ImageProgressCallback, PlatformAdapter } from './adapters/types'\nimport { BaijiahaoAdapter } from './adapters/platforms/baijiahao'\nimport { BilibiliAdapter } from './adapters/platforms/bilibili'\nimport { CnblogsAdapter } from './adapters/platforms/cnblogs'\nimport { CSDNAdapter } from './adapters/platforms/csdn'\nimport { Cto51Adapter } from './adapters/platforms/cto51'\nimport { DouyinAdapter } from './adapters/platforms/douyin'\nimport { JuejinAdapter } from './adapters/platforms/juejin'\nimport { NeteaseAdapter } from './adapters/platforms/netease'\nimport { SohuAdapter } from './adapters/platforms/sohu'\nimport { TencentAdapter } from './adapters/platforms/tencent'\nimport { ToutiaoAdapter } from './adapters/platforms/toutiao'\nimport { WeixinAdapter } from './adapters/platforms/weixin'\n\ntype AdapterConstructor = new () => PlatformAdapter\n\nconst ADAPTERS: Record<string, AdapterConstructor> = {\n baijiahao: BaijiahaoAdapter,\n bilibili: BilibiliAdapter,\n cnblogs: CnblogsAdapter,\n csdn: CSDNAdapter,\n '51cto': Cto51Adapter,\n douyin: DouyinAdapter,\n juejin: JuejinAdapter,\n netease: NeteaseAdapter,\n sohu: SohuAdapter,\n tencent: TencentAdapter,\n toutiao: ToutiaoAdapter,\n weixin: WeixinAdapter,\n}\n\nexport interface PublishArticleInput {\n title: string\n description?: string\n body?: string\n content?: string\n markdown?: string\n html?: string\n cover?: string\n tags?: string[]\n category?: string\n images?: Article['images']\n video?: Article['video']\n source?: Article['source']\n}\n\nexport interface PublishRequestOptions {\n onImageProgress?: ImageProgressCallback\n}\n\nexport interface PublishRequest {\n platform: string\n article: Article | PublishArticleInput\n runtime: RuntimeInterface\n onEvent?: PublishEventHandler\n options?: PublishRequestOptions\n}\n\nexport interface GetStatusRequest {\n platform: string\n runtime: RuntimeInterface\n statusRef: PublishStatusRef\n}\n\nexport interface CheckAuthRequest {\n platform: string\n runtime: RuntimeInterface\n}\n\nexport function getSupportedPlatforms(): string[] {\n return Object.keys(ADAPTERS)\n}\n\nexport function createAdapter(platform: string): PlatformAdapter | null {\n const Adapter = ADAPTERS[platform]\n return Adapter ? new Adapter() : null\n}\n\nexport async function publish(request: PublishRequest): Promise<PublishResult> {\n const adapter = createAdapter(request.platform)\n if (!adapter) {\n return {\n platform: request.platform,\n state: 'failed',\n error: { message: 'Platform not found', code: 'unsupported' },\n timestamp: Date.now(),\n }\n }\n\n try {\n await adapter.init(request.runtime)\n const result = await adapter.publish(normalizeArticle(request.article), {\n ...request.options,\n onEvent: request.onEvent,\n })\n return toPublishResult(request.platform, result)\n } catch (error) {\n return failedResult(request.platform, error)\n }\n}\n\nexport async function checkAuth(request: CheckAuthRequest): Promise<AuthResult> {\n const adapter = createAdapter(request.platform)\n if (!adapter) {\n return { isAuthenticated: false, error: 'Platform not found' }\n }\n\n try {\n await adapter.init(request.runtime)\n const auth = await adapter.checkAuth()\n if (!auth.isAuthenticated && !auth.error) {\n return { ...auth, error: 'Not authenticated' }\n }\n return auth\n } catch (error) {\n return { isAuthenticated: false, error: error instanceof Error ? error.message : String(error) }\n }\n}\n\nexport async function getStatus(request: GetStatusRequest): Promise<PublishStatusResult> {\n const platform = request.platform\n const adapter = createAdapter(platform)\n if (!adapter) {\n return {\n platform,\n state: 'unknown',\n message: 'Platform not found',\n checkedAt: Date.now(),\n }\n }\n\n if (request.statusRef.platform !== platform) {\n return {\n platform,\n state: 'unknown',\n message: `statusRef belongs to ${request.statusRef.platform}`,\n checkedAt: Date.now(),\n }\n }\n\n try {\n await adapter.init(request.runtime)\n if (!adapter.getStatus) {\n return {\n platform,\n state: 'unknown',\n message: 'Adapter status query is not implemented',\n checkedAt: Date.now(),\n }\n }\n return await adapter.getStatus(request.statusRef)\n } catch (error) {\n return {\n platform,\n state: 'unknown',\n message: error instanceof Error ? error.message : String(error),\n checkedAt: Date.now(),\n }\n }\n}\n\nfunction normalizeArticle(article: Article | PublishArticleInput): Article {\n const input = article as PublishArticleInput & Partial<Article>\n const markdown = input.markdown ?? input.body ?? input.content ?? ''\n\n return {\n title: input.title,\n markdown,\n html: input.html,\n summary: input.summary ?? input.description,\n cover: input.cover,\n tags: input.tags,\n category: input.category,\n images: input.images,\n video: input.video,\n source: input.source,\n }\n}\n\nfunction toPublishResult(platform: string, result: SyncResult): PublishResult {\n if (!result.success) {\n return {\n platform,\n state: 'failed',\n error: { message: result.error || result.message || 'Publish failed' },\n timestamp: result.timestamp || Date.now(),\n }\n }\n\n const statusRef = createStatusRef(platform, result)\n if (!statusRef.id && !statusRef.url) {\n return {\n platform,\n state: 'failed',\n error: { message: 'Publish succeeded but adapter did not return a statusRef source', code: 'missing_status_ref' },\n timestamp: result.timestamp || Date.now(),\n }\n }\n\n return {\n platform,\n state: 'succeeded',\n statusRef,\n postId: result.postId,\n postUrl: result.postUrl,\n timestamp: result.timestamp || Date.now(),\n }\n}\n\nfunction createStatusRef(platform: string, result: SyncResult): PublishStatusRef {\n const kind = typeof result.statusData?.kind === 'string'\n ? result.statusData.kind\n : getStatusKind(platform)\n\n return {\n platform,\n kind,\n id: result.postId,\n url: result.postUrl,\n data: result.statusData,\n }\n}\n\nfunction getStatusKind(platform: string): string {\n const kinds: Record<string, string> = {\n bilibili: 'opus',\n cnblogs: 'article',\n douyin: 'note',\n weixin: 'appmsg',\n }\n return kinds[platform] || 'article'\n}\n\nfunction failedResult(platform: string, error: unknown): PublishResult {\n return {\n platform,\n state: 'failed',\n error: { message: error instanceof Error ? error.message : String(error) },\n timestamp: Date.now(),\n }\n}\n","export { checkAuth, getStatus, publish } from './publish'\nexport { createExtensionRuntime } from './runtime'\nexport type {\n CheckAuthRequest,\n GetStatusRequest,\n PublishArticleInput,\n PublishRequest,\n PublishRequestOptions,\n} from './publish'\nexport type {\n Article,\n AuthResult,\n PublishError,\n PublishEvent,\n PublishEventHandler,\n PublishResult,\n PublishState,\n PublishStatusRef,\n PublishStatusResult,\n PublishStatusState,\n} from './types'\nexport type {\n RuntimeConfig,\n RuntimeInterface,\n} from './runtime/interface'\n\n// Version\nexport const VERSION = '1.0.6'\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skrillex1224/chrome-article-publish-extension",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Chrome-extension runtime article publishing adapters for Chinese content platforms.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"check:image-normalization": "pnpm build:npm && node scripts/check-image-file-normalization.mjs",
|
|
35
35
|
"check:juejin-status": "pnpm build:npm && node scripts/check-juejin-status.mjs",
|
|
36
36
|
"check:netease-content": "pnpm build:npm && node scripts/check-netease-content-normalization.mjs",
|
|
37
|
+
"check:no-field-truncation": "node scripts/check-no-field-truncation.mjs",
|
|
37
38
|
"check:public-api": "pnpm build:npm && node scripts/check-public-api.mjs",
|
|
38
39
|
"check:toutiao-status": "pnpm build:npm && node scripts/check-toutiao-status-url.mjs",
|
|
39
40
|
"check:tencent-auth": "pnpm build:npm && node scripts/check-tencent-auth-silent.mjs",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"check:tab-group": "pnpm build:npm && node scripts/check-runtime-tab-group.mjs && node scripts/check-runtime-managed-tab.mjs && node scripts/check-adapters-use-managed-tabs.mjs",
|
|
42
43
|
"check:weixin-content": "pnpm build:npm && node scripts/check-weixin-content-normalization.mjs",
|
|
43
44
|
"e2e:check-auth": "node scripts/e2e-check-auth.mjs",
|
|
44
|
-
"zip:extension": "pnpm build:extension && rm -f chrome-article-publish-extension-1.0.
|
|
45
|
+
"zip:extension": "pnpm build:extension && rm -f chrome-article-publish-extension-1.0.6.zip && cd dist && zip -qr ../chrome-article-publish-extension-1.0.6.zip .",
|
|
45
46
|
"zip:extension-app": "pnpm build:npm && pnpm --filter chrome-article-publish-extension zip",
|
|
46
47
|
"prepack": "pnpm build:npm",
|
|
47
48
|
"prepublishOnly": "pnpm build:npm",
|