@@ -4,6 +4,7 @@ const PLATFORMS = [
44 { id : 'juejin' , name : 'Juejin' , icon : 'https://lf-web-assets.juejin.cn/obj/juejin-web/xitu_juejin_web/static/favicons/favicon-32x32.png' , url : 'https://juejin.cn' , publishUrl : 'https://juejin.cn/editor/drafts/new' } ,
55 { 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' } ,
66 { id : 'zhihu' , name : 'Zhihu' , icon : 'https://static.zhihu.com/heifetz/favicon.ico' , url : 'https://www.zhihu.com' , publishUrl : 'https://zhuanlan.zhihu.com/write' } ,
7+ { 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' } ,
78]
89
910// 当前同步任务的 Tab Group ID
@@ -90,6 +91,15 @@ const LOGIN_CHECK_CONFIG = {
9091 avatar : response ?. avatar_url ,
9192 } ) ,
9293 } ,
94+ toutiao : {
95+ api : 'https://mp.toutiao.com/mp/agw/media/get_media_info' ,
96+ method : 'GET' ,
97+ checkLogin : ( response ) => response ?. err_no === 0 && response ?. data ?. media ?. display_name ,
98+ getUserInfo : ( response ) => ( {
99+ username : response ?. data ?. media ?. display_name ,
100+ avatar : response ?. data ?. media ?. https_avatar_url ,
101+ } ) ,
102+ } ,
93103}
94104
95105// 消息监听
@@ -172,7 +182,7 @@ async function checkPlatformLogin(platform) {
172182
173183 let data = null
174184 const contentType = response . headers . get ( 'content-type' ) || ''
175- if ( contentType . includes ( 'application/json' ) ) {
185+ if ( contentType . includes ( 'application/json' ) || contentType . includes ( 'text/plain' ) ) {
176186 try { data = await response . json ( ) } catch ( e ) { data = null }
177187 }
178188
@@ -243,30 +253,51 @@ async function checkLoginByCookie(platformId, config) {
243253 }
244254 }
245255
246- // 从微信公众号页面抓取用户信息
256+ // 从页面抓取用户信息
247257 if ( config . fetchUserInfoFromPage && config . userInfoUrl ) {
248258 try {
249259 const response = await fetch ( config . userInfoUrl , {
250260 method : 'GET' ,
251261 credentials : 'include'
252262 } )
253263 const html = await response . text ( )
254- // 从 HTML 中提取公众号名称
255- const nameMatch = html . match ( / n i c k _ n a m e \s * [: = ] \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / i) ||
256- html . match ( / < s p a n [ ^ > ] * c l a s s = " n i c k n a m e " [ ^ > ] * > ( [ ^ < ] + ) < \/ s p a n > / i)
257- if ( nameMatch ) {
258- username = nameMatch [ 1 ]
264+
265+ // 头条号用户信息提取
266+ if ( platformId === 'toutiao' ) {
267+ // 尝试从页面中提取用户名
268+ const nameMatch = html . match ( / \" n a m e \" \s * : \s * \" ( [ ^ " ] + ) \" / i) ||
269+ html . match ( / s c r e e n _ n a m e [ \" ' ] ? \s * [: = ] \s * [ \" ' ] ( [ ^ \" ' ] + ) [ \" ' ] / i) ||
270+ html . match ( / < s p a n [ ^ > ] * c l a s s = " [ ^ " ] * n a m e [ ^ " ] * " [ ^ > ] * > ( [ ^ < ] + ) < \/ s p a n > / i)
271+ if ( nameMatch ) {
272+ username = nameMatch [ 1 ]
273+ }
274+ // 尝试从页面中提取头像
275+ const avatarMatch = html . match ( / \" a v a t a r _ u r l \" \s * : \s * \" ( [ ^ " ] + ) \" / i) ||
276+ html . match ( / a v a t a r [ \" ' ] ? \s * [: = ] \s * [ \" ' ] ( [ ^ \" ' ] + ) [ \" ' ] / i)
277+ if ( avatarMatch ) {
278+ avatar = avatarMatch [ 1 ] . replace ( / \\ / g, '' )
279+ }
280+ console . log ( `[COSE] ${ platformId } 用户信息:` , username , avatar ? '有头像' : '无头像' )
259281 }
260- // 从 HTML 中提取头像
261- const avatarMatch = html . match ( / h e a d _ i m g \s * [: = ] \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / i ) ||
262- html . match ( / < i m g [ ^ > ] * c l a s s = " a v a t a r " [ ^ > ] * s r c = " ( [ ^ " ] + ) " / i )
263- if ( avatarMatch ) {
264- avatar = avatarMatch [ 1 ] . replace ( / \\ x 2 6 a m p ; / g , '&' ) . replace ( / \\ / g , '' )
265- if ( ! avatar . startsWith ( 'http' ) ) {
266- avatar = 'https://mp.weixin.qq.com' + avatar
282+ // 微信公众号用户信息提取
283+ else {
284+ // 从 HTML 中提取公众号名称
285+ const nameMatch = html . match ( / n i c k _ n a m e \s * [: = ] \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / i ) ||
286+ html . match ( / < s p a n [ ^ > ] * c l a s s = " n i c k n a m e " [ ^ > ] * > ( [ ^ < ] + ) < \/ s p a n > / i )
287+ if ( nameMatch ) {
288+ username = nameMatch [ 1 ]
267289 }
290+ // 从 HTML 中提取头像
291+ const avatarMatch = html . match ( / h e a d _ i m g \s * [: = ] \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / i) ||
292+ html . match ( / < i m g [ ^ > ] * c l a s s = " a v a t a r " [ ^ > ] * s r c = " ( [ ^ " ] + ) " / i)
293+ if ( avatarMatch ) {
294+ avatar = avatarMatch [ 1 ] . replace ( / \\ x 2 6 a m p ; / g, '&' ) . replace ( / \\ / g, '' )
295+ if ( ! avatar . startsWith ( 'http' ) ) {
296+ avatar = 'https://mp.weixin.qq.com' + avatar
297+ }
298+ }
299+ console . log ( `[COSE] ${ platformId } 用户信息:` , username , avatar ? '有头像' : '无头像' )
268300 }
269- console . log ( `[COSE] ${ platformId } 用户信息:` , username , avatar ? '有头像' : '无头像' )
270301 } catch ( e ) {
271302 console . log ( `[COSE] ${ platformId } 获取用户信息失败:` , e . message )
272303 }
@@ -687,6 +718,41 @@ function fillContentOnPage(content, platformId) {
687718 else if ( host . includes ( 'zhihu.com' ) ) {
688719 console . log ( '[COSE] 知乎由导入文档功能处理' )
689720 }
721+ // 今日头条
722+ else if ( host . includes ( 'toutiao.com' ) ) {
723+ // 填充标题 - 头条使用 textarea 作为标题输入
724+ const titleInput = await waitFor ( 'textarea[placeholder*="标题"], input[placeholder*="标题"], .editor-title textarea, .title-input textarea' )
725+ if ( titleInput ) {
726+ titleInput . focus ( )
727+ // 使用 nativeInputValueSetter 触发 React 状态更新
728+ const nativeInputValueSetter = Object . getOwnPropertyDescriptor ( window . HTMLTextAreaElement . prototype , 'value' ) ?. set
729+ || Object . getOwnPropertyDescriptor ( window . HTMLInputElement . prototype , 'value' ) ?. set
730+ if ( nativeInputValueSetter ) {
731+ nativeInputValueSetter . call ( titleInput , title )
732+ } else {
733+ titleInput . value = title
734+ }
735+ titleInput . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) )
736+ titleInput . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) )
737+ console . log ( '[COSE] 头条标题填充成功' )
738+ } else {
739+ console . log ( '[COSE] 头条未找到标题输入框' )
740+ }
741+
742+ // 等待编辑器加载
743+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
744+
745+ // 头条使用富文本编辑器
746+ const editor = document . querySelector ( '.ProseMirror, [contenteditable="true"], .editor-content' )
747+ if ( editor ) {
748+ editor . focus ( )
749+ editor . innerHTML = body || contentToFill . replace ( / \n / g, '<br>' )
750+ editor . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) )
751+ console . log ( '[COSE] 头条内容填充成功' )
752+ } else {
753+ console . log ( '[COSE] 头条未找到编辑器' )
754+ }
755+ }
690756 // 通用处理
691757 else {
692758 const titleSelectors = [ 'input[placeholder*="标题"]' , 'input[name="title"]' , 'textarea[placeholder*="标题"]' ]
0 commit comments