diff --git a/README.md b/README.md index 73eb758..02c4003 100644 --- a/README.md +++ b/README.md @@ -35,17 +35,16 @@ _**C**reate **O**nce **S**ync **E**verywhere_ - 微信公众号同步时完整保留渲染样式并自动保存为草稿 -## 支持的平台 +## 已支持的平台 -| 平台 | 说明 | -|------|------| -| CSDN | 自动填充 Markdown 内容 | -| 掘金 | 自动填充 Markdown 内容 | -| 微信公众号 | 保留完整样式,自动保存草稿 | -| 知乎 | 自动填充 Markdown 内容 | -| 今日头条 | 自动填充 Markdown 内容 | +- 微信公众号 +- 今日头条 +- 知乎 +- CSDN +- 掘金 +- 思否 -更多想要添加的平台欢迎提 [Issue](https://github.com/doocs/cose/issues) 。 +更多想要添加的平台欢迎提 [Issue](https://github.com/doocs/cose/issues) ! ## 开发者模式测试 diff --git a/src/background.js b/src/background.js index deda233..128ce48 100644 --- a/src/background.js +++ b/src/background.js @@ -5,6 +5,7 @@ const PLATFORMS = [ { id: 'wechat', name: 'WeChat', icon: 'https://res.wx.qq.com/a/wx_fed/assets/res/NTI4MWU5.ico', url: 'https://mp.weixin.qq.com', publishUrl: 'https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit_v2&action=edit&isNew=1&type=10' }, { id: 'zhihu', name: 'Zhihu', icon: 'https://static.zhihu.com/heifetz/favicon.ico', url: 'https://www.zhihu.com', publishUrl: 'https://zhuanlan.zhihu.com/write' }, { id: 'toutiao', name: 'Toutiao', icon: 'https://sf3-cdn-tos.toutiaostatic.com/obj/eden-cn/uhbfnupkbps/toutiao_favicon.ico', url: 'https://mp.toutiao.com', publishUrl: 'https://mp.toutiao.com/profile_v4/graphic/publish' }, + { id: 'segmentfault', name: 'SegmentFault', icon: 'https://static.segmentfault.com/main_site_next/prod/favicon.ico', url: 'https://segmentfault.com', publishUrl: 'https://segmentfault.com/write' }, ] // 当前同步任务的 Tab Group ID @@ -100,6 +101,13 @@ const LOGIN_CHECK_CONFIG = { avatar: response?.data?.media?.https_avatar_url, }), }, + segmentfault: { + useCookie: true, + cookieUrl: 'https://segmentfault.com', + cookieNames: ['PHPSESSID'], + fetchUserInfoFromPage: true, + userInfoUrl: 'https://segmentfault.com/write', + }, } // 消息监听 @@ -279,8 +287,32 @@ async function checkLoginByCookie(platformId, config) { } console.log(`[COSE] ${platformId} 用户信息:`, username, avatar ? '有头像' : '无头像') } + // 思否用户信息提取(从 __NEXT_DATA__ 中获取) + // 注意:思否的 PHPSESSID 即使未登录也存在,必须通过用户信息判断登录状态 + else if (platformId === 'segmentfault') { + // 从 __NEXT_DATA__ 中提取用户信息 + const nextDataMatch = html.match(/]*>([^<]+)<\/script>/i) + if (nextDataMatch) { + try { + const nextData = JSON.parse(nextDataMatch[1]) + const sessionUser = nextData?.props?.pageProps?.initialState?.global?.sessionUser?.user + if (sessionUser && sessionUser.name) { + username = sessionUser.name + avatar = sessionUser.avatar_url || '' + } + } catch (e) { + console.log(`[COSE] ${platformId} 解析 NEXT_DATA 失败:`, e.message) + } + } + // 思否:只有成功提取到用户名才算已登录 + if (!username) { + console.log(`[COSE] ${platformId} 未登录或无法获取用户信息`) + return { loggedIn: false } + } + console.log(`[COSE] ${platformId} 用户信息:`, username, avatar ? '有头像' : '无头像') + } // 微信公众号用户信息提取 - else { + else if (platformId === 'wechat') { // 从 HTML 中提取公众号名称 const nameMatch = html.match(/nick_name\s*[:=]\s*["']([^"']+)["']/i) || html.match(/]*class="nickname"[^>]*>([^<]+)<\/span>/i) @@ -749,6 +781,41 @@ function fillContentOnPage(content, platformId) { console.log('[COSE] 头条未找到编辑器') } } + // 思否 SegmentFault + else if (host.includes('segmentfault.com')) { + // 填充标题 + const titleInput = await waitFor('input#title, input[placeholder*="标题"]') + if (titleInput) { + titleInput.focus() + titleInput.value = title + titleInput.dispatchEvent(new Event('input', { bubbles: true })) + titleInput.dispatchEvent(new Event('change', { bubbles: true })) + console.log('[COSE] 思否标题填充成功') + } else { + console.log('[COSE] 思否未找到标题输入框') + } + + // 等待编辑器加载 + await new Promise(resolve => setTimeout(resolve, 1000)) + + // 思否使用 CodeMirror 编辑器 + const cmElement = document.querySelector('.CodeMirror') + if (cmElement && cmElement.CodeMirror) { + cmElement.CodeMirror.setValue(contentToFill) + console.log('[COSE] 思否 CodeMirror 填充成功') + } else { + // 降级到 textarea + const textarea = document.querySelector('textarea') + if (textarea) { + textarea.focus() + textarea.value = contentToFill + textarea.dispatchEvent(new Event('input', { bubbles: true })) + console.log('[COSE] 思否 textarea 填充成功') + } else { + console.log('[COSE] 思否 未找到编辑器') + } + } + } // 通用处理 else { const titleSelectors = ['input[placeholder*="标题"]', 'input[name="title"]', 'textarea[placeholder*="标题"]'] diff --git a/src/inject.js b/src/inject.js index a8fdbf6..60166f8 100644 --- a/src/inject.js +++ b/src/inject.js @@ -68,6 +68,7 @@ { id: 'wechat', name: 'WeChat', icon: 'https://res.wx.qq.com/a/wx_fed/assets/res/NTI4MWU5.ico', title: '微信公众号', type: 'wechat' }, { id: 'zhihu', name: 'Zhihu', icon: 'https://static.zhihu.com/heifetz/favicon.ico', title: '知乎', type: 'zhihu' }, { id: 'toutiao', name: 'Toutiao', icon: 'https://sf3-cdn-tos.toutiaostatic.com/obj/eden-cn/uhbfnupkbps/toutiao_favicon.ico', title: '今日头条', type: 'toutiao' }, + { id: 'segmentfault', name: 'SegmentFault', icon: 'https://youke2.picui.cn/s1/2025/12/21/6947b2935a41e.ico', title: '思否', type: 'segmentfault' }, ] // 暴露 $cose 全局对象 @@ -93,17 +94,21 @@ const result = await sendMessage('CHECK_PLATFORM_STATUS', { platforms: PLATFORMS }) const status = result?.status || {} - const accounts = PLATFORMS.map(p => ({ - uid: p.id, - type: p.type, - title: p.title, - displayName: status[p.id]?.username || p.title, - icon: p.icon, - avatar: status[p.id]?.avatar, - home: '', - checked: false, - loggedIn: status[p.id]?.loggedIn || false, - })) + const accounts = PLATFORMS.map(p => { + const platformStatus = status[p.id] || {} + const isLoggedIn = platformStatus.loggedIn || false + return { + uid: p.id, + type: p.type, + title: p.title, + displayName: isLoggedIn ? (platformStatus.username || p.title) : p.title, + icon: p.icon, + avatar: platformStatus.avatar, + home: '', + checked: false, + loggedIn: isLoggedIn, + } + }) if (typeof callback === 'function') { callback(accounts) diff --git a/src/platforms/index.js b/src/platforms/index.js index 5424acf..96f16a0 100644 --- a/src/platforms/index.js +++ b/src/platforms/index.js @@ -47,6 +47,15 @@ const PLATFORMS = [ title: '今日头条', type: 'toutiao', }, + { + id: 'segmentfault', + name: 'SegmentFault', + icon: 'https://static.segmentfault.com/main_site_next/prod/favicon.ico', + url: 'https://segmentfault.com', + publishUrl: 'https://segmentfault.com/write', + title: '思否', + type: 'segmentfault', + }, ] // 登录检测配置 @@ -94,6 +103,13 @@ const LOGIN_CHECK_CONFIG = { avatar: response?.data?.media?.https_avatar_url, }), }, + segmentfault: { + useCookie: true, + cookieUrl: 'https://segmentfault.com', + cookieNames: ['PHPSESSID'], + fetchUserInfoFromPage: true, + userInfoUrl: 'https://segmentfault.com/write', + }, } // 根据 hostname 获取平台填充函数 @@ -103,6 +119,7 @@ function getPlatformFiller(hostname) { if (hostname.includes('mp.weixin.qq.com')) return 'wechat' if (hostname.includes('zhihu.com')) return 'zhihu' if (hostname.includes('toutiao.com')) return 'toutiao' + if (hostname.includes('segmentfault.com')) return 'segmentfault' return 'generic' } diff --git a/src/platforms/segmentfault.js b/src/platforms/segmentfault.js new file mode 100644 index 0000000..7cafb8d --- /dev/null +++ b/src/platforms/segmentfault.js @@ -0,0 +1,61 @@ +// 思否平台配置 +const SegmentFaultPlatform = { + id: 'segmentfault', + name: 'SegmentFault', + icon: 'https://static.segmentfault.com/main_site_next/prod/favicon.ico', + url: 'https://segmentfault.com', + publishUrl: 'https://segmentfault.com/write', + title: '思否', + type: 'segmentfault', +} + +// 思否登录检测配置 +const SegmentFaultLoginConfig = { + useCookie: true, + cookieUrl: 'https://segmentfault.com', + cookieNames: ['PHPSESSID'], + fetchUserInfoFromPage: true, + userInfoUrl: 'https://segmentfault.com/write', +} + +// 思否内容填充函数 +async function fillSegmentFaultContent(content, waitFor, setInputValue) { + const { title, body, markdown } = content + const contentToFill = markdown || body || '' + + // 填充标题 + const titleInput = await waitFor('input#title, input[placeholder*="标题"]') + if (titleInput) { + titleInput.focus() + titleInput.value = title + titleInput.dispatchEvent(new Event('input', { bubbles: true })) + titleInput.dispatchEvent(new Event('change', { bubbles: true })) + console.log('[COSE] 思否标题填充成功') + } + + // 等待编辑器加载 + await new Promise(resolve => setTimeout(resolve, 1000)) + + // 思否使用 CodeMirror 编辑器 + const cmElement = document.querySelector('.CodeMirror') + if (cmElement && cmElement.CodeMirror) { + cmElement.CodeMirror.setValue(contentToFill) + console.log('[COSE] 思否 CodeMirror 填充成功') + } else { + // 降级到 textarea + const textarea = document.querySelector('textarea') + if (textarea) { + textarea.focus() + textarea.value = contentToFill + textarea.dispatchEvent(new Event('input', { bubbles: true })) + console.log('[COSE] 思否 textarea 填充成功') + } else { + console.log('[COSE] 思否 未找到编辑器') + } + } +} + +// 导出 +if (typeof module !== 'undefined' && module.exports) { + module.exports = { SegmentFaultPlatform, SegmentFaultLoginConfig, fillSegmentFaultContent } +}