publishport-opencli 1.8.5-pp.22 → 1.8.5-pp.23
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/clis/jike/auth.js +4 -2
- package/clis/jike/feed.js +3 -3
- package/clis/jike/like.js +7 -11
- package/clis/jike/notifications.js +2 -2
- package/clis/jike/repost.js +22 -23
- package/clis/jike/search.js +3 -3
- package/package.json +1 -1
package/clis/jike/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{AuthRequiredError as
|
|
1
|
+
import{AuthRequiredError as L,CommandExecutionError as K}from"@jackwener/opencli/errors";import{registerSiteAuthCommands as Q}from"../_shared/site-auth.js";const N=`(async () => {
|
|
2
2
|
try {
|
|
3
3
|
const token = localStorage.getItem('JK_ACCESS_TOKEN') || '';
|
|
4
4
|
if (!token) return { kind: 'auth', detail: 'Jike JK_ACCESS_TOKEN missing from localStorage (anonymous)' };
|
|
@@ -14,4 +14,6 @@ import{AuthRequiredError as N,CommandExecutionError as L}from"@jackwener/opencli
|
|
|
14
14
|
} catch (e) {
|
|
15
15
|
return { kind: 'exception', detail: String(e && e.message || e) };
|
|
16
16
|
}
|
|
17
|
-
})()`;async function
|
|
17
|
+
})()`;async function T(z){await z.goto("https://web.okjike.com/");await z.wait(2);let F="";for(let G=0;G<30;G++){F=await z.evaluate("() => location.href");if(F.includes("okjike.com"))break;await z.wait(0.5)}if(!F.includes("okjike.com")){await z.screenshot({path:"/tmp/jike_whoami_nav_debug.png"});throw new K(`Jike whoami: navigation never settled on okjike.com (landed on ${F}). Debug screenshot: /tmp/jike_whoami_nav_debug.png`)}let D=await z.evaluate(N);for(let G=0;G<30&&D?.kind==="auth";G++){await z.wait(0.5);D=await z.evaluate(N)}if(D?.kind==="auth")throw new L("web.okjike.com",D.detail);if(D?.kind==="http")throw new K(`HTTP ${D.httpStatus} from Jike users/profile`);if(D?.kind==="exception"){const G=await z.evaluate("() => location.href");if(/^data:/.test(G)||/\/login/.test(F))throw new L("web.okjike.com","Jike session anonymous (redirected to /login)");throw new K(`Jike whoami failed: ${D.detail}`)}if(!D?.ok)throw new K(`Unexpected Jike probe: ${JSON.stringify(D)}`);return{user_id:D.user_id,screen_name:D.screen_name,username:D.username}}Q({site:"jike",domain:"web.okjike.com",loginUrl:"https://web.okjike.com/login",columns:["user_id","screen_name","username"],verify:T,quickCheck:async(z)=>{await z.goto("https://web.okjike.com/robots.txt");return{logged_in:await z.evaluate(`(() => {
|
|
18
|
+
try { return !!localStorage.getItem('JK_ACCESS_TOKEN'); } catch { return false; }
|
|
19
|
+
})()`)===!0}},poll:async(z)=>{const F=await z.evaluate(N);if(!F?.ok)throw new L("web.okjike.com","Waiting for Jike login");return{user_id:F.user_id,screen_name:F.screen_name,username:F.username}}});
|
package/clis/jike/feed.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{cli as
|
|
2
|
-
${
|
|
1
|
+
import{cli as F,Strategy as G}from"@jackwener/opencli/registry";import{AuthRequiredError as H,EmptyResultError as I}from"@jackwener/opencli/errors";import{getPostDataJs as K}from"./utils.js";F({site:"jike",name:"feed",access:"read",description:"即刻首页动态流",domain:"web.okjike.com",strategy:G.COOKIE,browser:!0,args:[{name:"limit",type:"int",default:20}],columns:["id","author","content","likes","comments","time","url"],func:async(f,C)=>{const z=C.limit||20;await f.goto("https://web.okjike.com");const B=async()=>{return await f.evaluate(`(() => {
|
|
2
|
+
${K}
|
|
3
3
|
|
|
4
4
|
const results = [];
|
|
5
5
|
const seen = new Set();
|
|
@@ -29,4 +29,4 @@ import{cli as v,Strategy as z}from"@jackwener/opencli/registry";import{getPostDa
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
return results;
|
|
32
|
-
})()`)};let f=await
|
|
32
|
+
})()`)};let b=await B();for(let v=0;v<30&&b.length===0;v++){await f.wait(0.5);b=await B()}if(b.length===0){const v=await f.evaluate("() => location.href");if(/\/login/.test(v)||/^data:/.test(v))throw new H("web.okjike.com","Jike feed requires login (redirected to /login)");throw new I("jike feed","No posts rendered on web.okjike.com within 15s. The page structure may have changed.")}if(b.length<z){await f.autoScroll({times:Math.ceil(z/10),delayMs:2000});b=await B()}return b.slice(0,z)}});
|
package/clis/jike/like.js
CHANGED
|
@@ -8,25 +8,21 @@ import{cli as m,Strategy as q}from"@jackwener/opencli/registry";m({site:"jike",n
|
|
|
8
8
|
return { ok: false, message: '未找到点赞按钮' };
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
// 已赞状态挂在内层 SVG 图标的类上(_likeIconLiked_),外层容器 div 的
|
|
12
|
+
// 类名点击前后不变——按容器类名判定会把成功点赞误报为失败(真机踩过)。
|
|
13
|
+
const isLiked = () => !!likeBtn.querySelector('[class*="_likeIconLiked_"]');
|
|
14
|
+
if (isLiked()) {
|
|
14
15
|
return { ok: true, message: '该帖子已赞过' };
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
const beforeCls = likeBtn.className;
|
|
19
|
-
|
|
18
|
+
const beforeCount = (likeBtn.textContent || '').trim();
|
|
20
19
|
likeBtn.click();
|
|
21
20
|
await new Promise(r => setTimeout(r, 1500));
|
|
22
21
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
if (afterCls !== beforeCls) {
|
|
22
|
+
// 验证:已赞图标出现或计数变化,任一即成功
|
|
23
|
+
if (isLiked() || (likeBtn.textContent || '').trim() !== beforeCount) {
|
|
26
24
|
return { ok: true, message: '点赞成功' };
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
// 类名未变化,无法确认点赞是否成功
|
|
30
26
|
return { ok: false, message: '点赞状态未确认,请手动检查' };
|
|
31
27
|
} catch (e) {
|
|
32
28
|
return { ok: false, message: e.toString() };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{cli as
|
|
1
|
+
import{cli as D,Strategy as E}from"@jackwener/opencli/registry";function F(d){if(!d)return"通知";const h=d.toUpperCase();if(h.includes("LIKE"))return"赞了你";if(h.includes("COMMENT"))return"评论了你";if(h.includes("FOLLOW"))return"关注了你";if(h.includes("REPOST"))return"转发了你";if(h.includes("MENTION"))return"提到了你";if(h.includes("REPLY"))return"回复了你";return d}D({site:"jike",name:"notifications",access:"read",description:"即刻通知",domain:"web.okjike.com",strategy:E.COOKIE,browser:!0,args:[{name:"limit",type:"int",default:20}],columns:["type","user","content","time"],func:async(d,h)=>{const z=h.limit||20;await d.goto("https://web.okjike.com/notification");for(let B=0;B<30;B++){if(await d.evaluate(`(() => document.querySelectorAll('[class*="_item_"]').length > 0)()`))break;await d.wait(0.5)}const C=await d.evaluate(`(() => {
|
|
2
2
|
// 从 React fiber 树中提取通知数据,向上最多走 15 层
|
|
3
3
|
function getNotificationData(element) {
|
|
4
4
|
for (const key of Object.keys(element)) {
|
|
@@ -57,7 +57,7 @@ import{cli as C,Strategy as D}from"@jackwener/opencli/registry";function E(d){if
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
return results;
|
|
60
|
-
})()`);if(
|
|
60
|
+
})()`);if(C.length>0)return C.map((q)=>({type:F(q.actionType),user:q.fromUser,content:q.content.replace(/\n/g," ").slice(0,100),time:q.time})).slice(0,z);await d.autoScroll({times:Math.ceil(z/10),delayMs:2000});return(await d.evaluate(`(() => {
|
|
61
61
|
const results = [];
|
|
62
62
|
const items = document.querySelectorAll('[class*="_item_"]');
|
|
63
63
|
|
package/clis/jike/repost.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{cli as
|
|
1
|
+
import{cli as E,Strategy as F}from"@jackwener/opencli/registry";E({site:"jike",name:"repost",access:"write",description:"转发即刻帖子",domain:"web.okjike.com",strategy:F.UI,browser:!0,args:[{name:"id",type:"string",required:!0,positional:!0,help:"帖子 ID"},{name:"text",positional:!0,type:"string",required:!1,help:"转发附言(可选)"}],columns:["status","message"],func:async(b,v)=>{await b.goto(`https://web.okjike.com/originalPost/${v.id}`);for(let h=0;h<30;h++){if(await b.evaluate(`(() => {
|
|
2
2
|
const actions = document.querySelector('[class*="_actions_"]');
|
|
3
3
|
if (!actions) return false;
|
|
4
4
|
const children = Array.from(actions.children).filter(c => c.offsetHeight > 0);
|
|
5
5
|
return !!children[2];
|
|
6
|
-
})()`))break;await b.wait(0.5)}const
|
|
6
|
+
})()`))break;await b.wait(0.5)}const A=await b.evaluate(`(async () => {
|
|
7
7
|
try {
|
|
8
8
|
const actions = document.querySelector('[class*="_actions_"]');
|
|
9
9
|
if (!actions) return { ok: false, message: '未找到操作栏' };
|
|
@@ -15,11 +15,11 @@ import{cli as B,Strategy as C}from"@jackwener/opencli/registry";B({site:"jike",n
|
|
|
15
15
|
} catch (e) {
|
|
16
16
|
return { ok: false, message: e.toString() };
|
|
17
17
|
}
|
|
18
|
-
})()`);if(!
|
|
18
|
+
})()`);if(!A.ok)return[{status:"failed",message:A.message}];await b.wait(1);for(let h=0;h<30;h++){if(await b.evaluate(`(() => {
|
|
19
19
|
return Array.from(document.querySelectorAll('button')).some(
|
|
20
20
|
b => b.textContent?.trim() === '转发动态'
|
|
21
21
|
);
|
|
22
|
-
})()`))break;await b.wait(0.5)}const
|
|
22
|
+
})()`))break;await b.wait(0.5)}const B=await b.evaluate(`(async () => {
|
|
23
23
|
try {
|
|
24
24
|
const btn = Array.from(document.querySelectorAll('button')).find(
|
|
25
25
|
b => b.textContent?.trim() === '转发动态'
|
|
@@ -30,13 +30,13 @@ import{cli as B,Strategy as C}from"@jackwener/opencli/registry";B({site:"jike",n
|
|
|
30
30
|
} catch (e) {
|
|
31
31
|
return { ok: false, message: e.toString() };
|
|
32
32
|
}
|
|
33
|
-
})()`);if(!
|
|
34
|
-
return !!document.querySelector('[contenteditable="true"]');
|
|
35
|
-
})()`))break;await b.wait(0.5)}const
|
|
33
|
+
})()`);if(!B.ok)return[{status:"failed",message:B.message}];await b.wait(2);if(v.text){for(let q=0;q<30;q++){if(await b.evaluate(`(() => {
|
|
34
|
+
return !!document.querySelector('[class*="Modal"] [contenteditable="true"], [class*="modal"] [contenteditable="true"]');
|
|
35
|
+
})()`))break;await b.wait(0.5)}const h=await b.evaluate(`(async () => {
|
|
36
36
|
try {
|
|
37
|
-
const textToInsert = ${JSON.stringify(
|
|
38
|
-
const editor = document.querySelector('[contenteditable="true"]');
|
|
39
|
-
if (!editor) return { ok: false, message: '
|
|
37
|
+
const textToInsert = ${JSON.stringify(v.text)};
|
|
38
|
+
const editor = document.querySelector('[class*="Modal"] [contenteditable="true"], [class*="modal"] [contenteditable="true"]');
|
|
39
|
+
if (!editor) return { ok: false, message: '未找到转发弹窗内的附言输入框' };
|
|
40
40
|
editor.focus();
|
|
41
41
|
const dt = new DataTransfer();
|
|
42
42
|
dt.setData('text/plain', textToInsert);
|
|
@@ -46,23 +46,22 @@ import{cli as B,Strategy as C}from"@jackwener/opencli/registry";B({site:"jike",n
|
|
|
46
46
|
await new Promise(r => setTimeout(r, 500));
|
|
47
47
|
return { ok: true };
|
|
48
48
|
} catch(e) { return { ok: false, message: '附言写入失败: ' + e.toString() }; }
|
|
49
|
-
})()`);if(!
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
})()`);if(!h.ok)return[{status:"failed",message:h.message}]}const D=`(() => {
|
|
50
|
+
return Array.from(document.querySelectorAll('button')).find(b => {
|
|
51
|
+
const text = b.textContent?.trim() || '';
|
|
52
|
+
if (text !== '转发动态' && text !== '发送' && text !== '发布') return false;
|
|
53
|
+
if (b.disabled) return false;
|
|
54
|
+
if (b.closest('[class*="Popover"]')) return false;
|
|
55
|
+
return !!(b.closest('[class*="Modal"], [class*="modal"], [class*="_submitButton_"]'));
|
|
56
|
+
});
|
|
57
|
+
})`;for(let h=0;h<30;h++){if(await b.evaluate(`(() => !!${D}())()`))break;await b.wait(0.5)}const z=await b.evaluate(`(async () => {
|
|
55
58
|
try {
|
|
56
59
|
await new Promise(r => setTimeout(r, 500));
|
|
57
|
-
const btn =
|
|
58
|
-
|
|
59
|
-
// 不匹配"转发动态",避免重复触发 Popover 菜单项
|
|
60
|
-
return (text === '发送' || text === '发布') && !b.disabled;
|
|
61
|
-
});
|
|
62
|
-
if (!btn) return { ok: false, message: '未找到发送按钮' };
|
|
60
|
+
const btn = ${D}();
|
|
61
|
+
if (!btn) return { ok: false, message: '未找到转发弹窗内的确认按钮' };
|
|
63
62
|
btn.click();
|
|
64
63
|
return { ok: true, message: '转发成功' };
|
|
65
64
|
} catch (e) {
|
|
66
65
|
return { ok: false, message: e.toString() };
|
|
67
66
|
}
|
|
68
|
-
})()`);if(
|
|
67
|
+
})()`);if(z.ok)await b.wait(3);return[{status:z.ok?"success":"failed",message:z.message}]}});
|
package/clis/jike/search.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{cli as
|
|
2
|
-
${
|
|
1
|
+
import{cli as C,Strategy as F}from"@jackwener/opencli/registry";import{EmptyResultError as G}from"@jackwener/opencli/errors";import{getPostDataJs as H}from"./utils.js";C({site:"jike",name:"search",access:"read",description:"搜索即刻帖子",domain:"web.okjike.com",strategy:F.COOKIE,browser:!0,args:[{name:"query",type:"string",required:!0,positional:!0,help:"即刻搜索关键词"},{name:"limit",type:"int",default:20}],columns:["id","author","content","likes","comments","time","url"],func:async(f,v)=>{const z=v.query,h=v.limit||20,B=encodeURIComponent(z);await f.goto(`https://web.okjike.com/search?q=${B}`);const q=async()=>{return await f.evaluate(`(() => {
|
|
2
|
+
${H}
|
|
3
3
|
|
|
4
4
|
const results = [];
|
|
5
5
|
const seen = new Set();
|
|
@@ -26,4 +26,4 @@ import{cli as B,Strategy as C}from"@jackwener/opencli/registry";import{getPostDa
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return results;
|
|
29
|
-
})()`)};let
|
|
29
|
+
})()`)};let b=await q();for(let A=0;A<30&&b.length===0;A++){await f.wait(0.5);b=await q()}if(b.length===0)throw new G("jike search",`No results rendered for "${z}" within 15s. The keyword may have no matches or the page structure changed.`);if(b.length<h){await f.autoScroll({times:Math.ceil(h/10),delayMs:2000});b=await q()}return b.slice(0,h)}});
|