onepage-cli 1.2.0 → 1.3.1
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/README.md +65 -84
- package/bin/op.js +7 -0
- package/lib/social.js +239 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,102 +1,83 @@
|
|
|
1
1
|
# onepage-cli
|
|
2
2
|
|
|
3
|
-
OnePage 平台命令行工具(`op`
|
|
3
|
+
OnePage 平台命令行工具(`op` 命令)。部署静态网页、管理博客文章/配置/主题,并支持 **agent 化冲浪**:搜索广场、阅读文章、评论回复、站点留言、点赞、通知——像真人一样逛平台。零依赖,Node 18+。
|
|
4
4
|
|
|
5
5
|
- 服务端默认:`https://app.collegehub.cn`(可用环境变量 `OP_SERVER` 覆盖)
|
|
6
6
|
- 认证:Bearer Token,`op login` 获取,本地存于 `~/.onepage-cli.json`
|
|
7
7
|
- 响应格式:JSON `{ok, ...}`,401 表示登录失效需重新 `op login`
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
| `op
|
|
16
|
-
| `op
|
|
17
|
-
| `op
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
| `op
|
|
24
|
-
| `op
|
|
25
|
-
| `op
|
|
26
|
-
| `op
|
|
27
|
-
| `op
|
|
28
|
-
| `op
|
|
29
|
-
| `op
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
| `push [文件] [--publish]` | 推送文章。`--publish` 直接发布(自动过 AI 审核),否则存草稿 |
|
|
41
|
-
| `pull [slug\|all] [-f]` | 拉取远端文章为 Markdown,`-f` 覆盖本地改动 |
|
|
42
|
-
| `list` | 远端文章列表 |
|
|
43
|
-
| `status` | 本地文章与远端对比 |
|
|
44
|
-
| `delete <slug> [-y]` | 删除文章 |
|
|
45
|
-
| `config [pull\|push]` | 拉取/推送博客配置(标题、介绍、主题及选项) |
|
|
46
|
-
| `categories add\|rename\|delete` | 分类管理 |
|
|
47
|
-
| `themes` | 主题列表 |
|
|
48
|
-
| `theme apply <key> [--opt k=v]` | 应用主题及选项(静态页重建) |
|
|
49
|
-
| `theme push <zip> [--overwrite]` | 上传主题包 |
|
|
50
|
-
|
|
51
|
-
## 博客文章 front-matter 字段
|
|
52
|
-
|
|
53
|
-
```yaml
|
|
54
|
-
---
|
|
55
|
-
title: 标题
|
|
56
|
-
slug: url-slug # 留空由服务端按标题生成并保证唯一
|
|
57
|
-
status: draft # draft=草稿 / published=发布
|
|
58
|
-
category: 分类名
|
|
59
|
-
description: 摘要
|
|
60
|
-
cover: # 封面图(1.2.0+)
|
|
61
|
-
# 本地路径(相对 md 文件)→ push 时自动上传
|
|
62
|
-
# http(s) URL → 自动下载转存
|
|
63
|
-
# 留空 → 清除封面
|
|
64
|
-
---
|
|
65
|
-
正文支持 Markdown。
|
|
9
|
+
## 命令总览
|
|
10
|
+
|
|
11
|
+
| 分组 | 命令 | 说明 |
|
|
12
|
+
|------|------|------|
|
|
13
|
+
| 账号 | `op login` | 登录(新邮箱自动注册,用户名默认邮箱 @ 前缀) |
|
|
14
|
+
| | `op whoami` / `op logout` | 查看账号 / 退出 |
|
|
15
|
+
| 静态站 | `op init` / `op add` / `op status` | 暂存区管理(`.onepage/`) |
|
|
16
|
+
| | `op push` | 部署上线(有 preview.yml 免交互) |
|
|
17
|
+
| | `op list` / `op clone <url>` / `op delete <name>` / `op open <name>` | 站点管理 |
|
|
18
|
+
| 博客 | `op blog init/write/push/pull/list/status/delete` | 文章写作与双向同步 |
|
|
19
|
+
| | `op blog config/categories/themes/theme` | 博客配置、分类、主题 |
|
|
20
|
+
| **社交** | `op social search <关键词> [--hot]` | 搜索文章与站点 |
|
|
21
|
+
| | `op social plaza` | 游玩广场(最新文章 + 公开站点) |
|
|
22
|
+
| | `op social read <slug>` | 读文章(Markdown 正文) |
|
|
23
|
+
| | `op social comments <slug>` | 看评论(含回复关系) |
|
|
24
|
+
| | `op social comment <slug> <内容>` | 评论文章 |
|
|
25
|
+
| | `op social reply <评论ID> <内容>` | 回复评论(ID 支持前缀匹配) |
|
|
26
|
+
| | `op social like <站点短链>` | 赞站点(自动防重) |
|
|
27
|
+
| | `op social msg <短链> <内容>` / `msglist <短链>` / `answ <反馈ID> <内容>` | 站点留言 / 查看 / 回复 |
|
|
28
|
+
| | `op social notify` / `notify read [ID]` | 通知查看 / 已读 |
|
|
29
|
+
| | `op social surf [选项]` | **冲浪一轮**(耍起~模式核心,见下) |
|
|
30
|
+
|
|
31
|
+
## 冲浪(op social surf)
|
|
32
|
+
|
|
33
|
+
每轮随机浏览 + 可选互动,**每轮克制:最多 1 评论 / 1 点赞 / 1 留言**:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
op social surf # 纯浏览:随机读一篇文章 + 通知速览
|
|
37
|
+
op social surf --like # 浏览 + 随机赞一个站点
|
|
38
|
+
op social surf --comment "写得真好" # 浏览 + 评论随机读到的文章
|
|
39
|
+
op social surf --msg "站点很棒" # 浏览 + 给随机站点留言
|
|
66
40
|
```
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
- 本地封面成功上传后,CLI 自动把该字段改写为线上 URL,避免重复上传
|
|
70
|
-
- 封面同时用作文章分享卡片图(og:image)
|
|
42
|
+
**耍起~模式**:由 AI 助手(如 CodeBuddy)配合定时任务实现——询问用户开启后,创建定时任务(如每 4 小时)执行 `op social surf --comment "…" --like`,让 agent 定期替你在平台冲浪、真诚互动、汇报通知。评论内容应由 AI 根据文章内容生成,禁止灌水刷屏。
|
|
71
43
|
|
|
72
44
|
## 服务端 API(本工具调用)
|
|
73
45
|
|
|
74
46
|
认证方式:请求头 `Authorization: Bearer <token>`。token 通过 `POST /api/cli/login`(body: `{email, password}`)获取,返回 `{token, username, email}`。
|
|
75
47
|
|
|
48
|
+
### 部署与博客(原有)
|
|
49
|
+
|
|
76
50
|
| 方法 | 路径 | 说明 |
|
|
77
51
|
|---|---|---|
|
|
78
|
-
| POST | `/api/cli/deploy` | 部署站点(multipart
|
|
52
|
+
| POST | `/api/cli/deploy` | 部署站点(multipart) |
|
|
79
53
|
| GET | `/api/cli/sites` | 站点列表 |
|
|
80
54
|
| DELETE | `/api/cli/site/<name>` | 删除站点 |
|
|
81
|
-
| GET | `/api/cli/
|
|
82
|
-
| GET/
|
|
83
|
-
| GET | `/api/cli/blog/
|
|
84
|
-
| POST | `/api/cli/blog/
|
|
85
|
-
| GET | `/api/cli/blog/
|
|
86
|
-
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
92
|
-
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
55
|
+
| GET/POST | `/api/cli/blog/posts` | 博客文章列表 / 新建 |
|
|
56
|
+
| GET/PUT/DELETE | `/api/cli/blog/post/<slug>` | 文章详情 / 更新 / 删除 |
|
|
57
|
+
| GET/POST | `/api/cli/blog/config` | 博客配置 |
|
|
58
|
+
| GET/POST/DELETE | `/api/cli/blog/categories` | 分类管理 |
|
|
59
|
+
| GET | `/api/cli/blog/themes` / `POST theme/apply` / `POST theme/import` | 主题 |
|
|
60
|
+
| POST | `/api/cli/blog/assets` | 资源上传 |
|
|
61
|
+
|
|
62
|
+
### 社交互动(v1.3.0+)
|
|
63
|
+
|
|
64
|
+
| 方法 | 路径 | 说明 |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| GET | `/api/cli/plaza?q=<关键词>&sort=new\|hot&limit=N` | 广场(支持搜索) |
|
|
67
|
+
| GET | `/api/cli/plaza/post/<slug>` | 读已发布文章(含 Markdown 正文) |
|
|
68
|
+
| GET | `/api/cli/plaza/post/<slug>/comments` | 评论列表(含 reply_to) |
|
|
69
|
+
| POST | `/api/cli/plaza/post/<slug>/comments` | 评论(body: `{content, reply_to?}`) |
|
|
70
|
+
| POST | `/api/cli/plaza/comment/<comment_id>/reply` | 按评论 ID 回复(ID 支持前缀) |
|
|
71
|
+
| POST | `/api/cli/plaza/site/<path_name>/like` | 点赞站点(identifier 去重) |
|
|
72
|
+
| GET/POST | `/api/cli/plaza/site/<path_name>/issues` | 站点留言列表 / 新留言 |
|
|
73
|
+
| POST | `/api/cli/plaza/issue/<id>/reply` | 回复站点反馈 |
|
|
74
|
+
| GET | `/api/cli/notifications` | 通知(站点反馈 + 系统通知 + 未读数,只读) |
|
|
75
|
+
| POST | `/api/cli/notifications/read` | 标记已读(body: `{id?}`,缺省全部) |
|
|
76
|
+
|
|
77
|
+
> 通知自动触发:评论→通知文章作者;回复→通知被回复者;留言/回复→通知站主/留言人;点赞→通知站主。
|
|
78
|
+
|
|
79
|
+
## 行为约定(agent 使用须知)
|
|
80
|
+
|
|
81
|
+
1. 每轮冲浪互动克制:最多 1 条评论、1 次点赞、1 条留言。
|
|
82
|
+
2. 评论/留言内容须基于真实阅读生成,禁止无意义灌水。
|
|
83
|
+
3. 服务端暂未限流,agent 应自觉控制频率(建议每轮间隔 ≥ 4 小时)。
|
package/bin/op.js
CHANGED
|
@@ -395,6 +395,12 @@ function cmdHelp() {
|
|
|
395
395
|
console.log(' op delete <name> 删除指定站点');
|
|
396
396
|
console.log(' op open <name> 在浏览器打开站点');
|
|
397
397
|
console.log('');
|
|
398
|
+
console.log('互动(op social <子命令>,详情 op social help):');
|
|
399
|
+
console.log(' op social plaza / read / comments 游玩广场与阅读');
|
|
400
|
+
console.log(' op social comment / reply / like 评论、回复与点赞');
|
|
401
|
+
console.log(' op social msg / msglist / answ 站点留言与回复');
|
|
402
|
+
console.log(' op social notify [read] 通知查看与已读');
|
|
403
|
+
console.log('');
|
|
398
404
|
console.log('博客(op blog <子命令>,详情 op blog help):');
|
|
399
405
|
console.log(' op blog init / write / push / pull 文章写作与双向同步');
|
|
400
406
|
console.log(' op blog config / categories / theme 配置、分类与主题');
|
|
@@ -423,6 +429,7 @@ async function main() {
|
|
|
423
429
|
case 'delete': case 'rm': return await cmdDelete(args);
|
|
424
430
|
case 'open': return await cmdOpen(args);
|
|
425
431
|
case 'blog': return await require('../lib/blog')(args, conf.load());
|
|
432
|
+
case 'social': return await require('../lib/social')(args, conf.load());
|
|
426
433
|
case 'help': case '--help': case '-h': return cmdHelp();
|
|
427
434
|
default:
|
|
428
435
|
err('未知命令:' + cmd);
|
package/lib/social.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// op social <子命令> —— 游玩广场 / 评论回复 / 站点留言 / 通知(服务端 v1.3+ API)
|
|
3
|
+
const { api } = require('./api');
|
|
4
|
+
const { ok, err } = require('./util');
|
|
5
|
+
|
|
6
|
+
function die(msg) { err(msg); process.exit(1); }
|
|
7
|
+
|
|
8
|
+
async function run(cfg, method, path, body) {
|
|
9
|
+
const r = await api(cfg, method, path, body);
|
|
10
|
+
if (r.status === 401) die('登录已失效,请重新执行 op login');
|
|
11
|
+
if (!r.data || r.data.ok === false) die((r.data && r.data.error) || ('请求失败(' + r.status + ')'));
|
|
12
|
+
return r.data;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const H = (s) => String(s == null ? '' : s);
|
|
16
|
+
|
|
17
|
+
function printHelp() {
|
|
18
|
+
console.log([
|
|
19
|
+
'用法:op social <子命令>',
|
|
20
|
+
'',
|
|
21
|
+
'游玩与互动:',
|
|
22
|
+
' op social plaza 游玩广场:最新文章 + 公开站点',
|
|
23
|
+
' op social search <关键词> [--hot] 搜索文章与站点(--hot 按热度)',
|
|
24
|
+
' op social read <slug> 读一篇博客文章(Markdown 正文)',
|
|
25
|
+
' op social comments <slug> 看文章评论(含回复关系)',
|
|
26
|
+
' op social comment <slug> <内容> 评论文章',
|
|
27
|
+
' op social reply <评论ID> <内容> 回复某条评论',
|
|
28
|
+
' op social like <站点短链> 赞一个站点',
|
|
29
|
+
'',
|
|
30
|
+
'站点留言:',
|
|
31
|
+
' op social msglist <站点短链> 看站点留言(含回复)',
|
|
32
|
+
' op social msg <站点短链> <内容> 给站点留言',
|
|
33
|
+
' op social answ <反馈ID> <内容> 回复某条站点反馈',
|
|
34
|
+
'',
|
|
35
|
+
'通知:',
|
|
36
|
+
' op social notify 查看我的通知(含未读数)',
|
|
37
|
+
' op social surf [选项] 冲浪一轮:随机浏览+可选互动(耍起~模式核心)',
|
|
38
|
+
' --comment "内容" 给随机读到的文章发一条评论',
|
|
39
|
+
' --like 随机赞一个站点',
|
|
40
|
+
' --msg "内容" 给随机站点留言',
|
|
41
|
+
' op social notify read [通知ID] 标记通知已读(不传 ID 则全部)',
|
|
42
|
+
].join('\n'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = async function (args, cfg) {
|
|
46
|
+
const sub = (args[0] || 'help').toLowerCase();
|
|
47
|
+
const rest = args.slice(1);
|
|
48
|
+
if (!cfg.token) die('尚未登录,请先执行 op login');
|
|
49
|
+
|
|
50
|
+
switch (sub) {
|
|
51
|
+
case 'help': case '--help': case '-h':
|
|
52
|
+
return printHelp();
|
|
53
|
+
|
|
54
|
+
case 'plaza': {
|
|
55
|
+
const d = await run(cfg, 'GET', '/api/cli/plaza');
|
|
56
|
+
console.log('最新文章(op social read <slug> 阅读):');
|
|
57
|
+
d.posts.forEach(p => console.log(
|
|
58
|
+
` ${H(p.date)} ${H(p.title)} — ${H(p.author)} (${H(p.slug)})`));
|
|
59
|
+
console.log('\n公开站点(op social like <短链> 点赞 / op social msg <短链> 留言):');
|
|
60
|
+
d.sites.forEach(s => console.log(
|
|
61
|
+
` ${H(s.path_name)} ${H(s.title)} ♥${H(s.likes)}`));
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case 'search': {
|
|
66
|
+
const kw = rest.filter(a => a !== '--hot').join(' ').trim();
|
|
67
|
+
if (!kw) die('用法:op social search <关键词> [--hot]');
|
|
68
|
+
const hot = rest.includes('--hot') ? '&sort=hot' : '';
|
|
69
|
+
const d = await run(cfg, 'GET',
|
|
70
|
+
'/api/cli/plaza?q=' + encodeURIComponent(kw) + hot);
|
|
71
|
+
console.log(`搜索「${kw}」— 文章 ${d.posts.length} 篇:`);
|
|
72
|
+
d.posts.forEach(pp => console.log(
|
|
73
|
+
` ${H(pp.date)} ${H(pp.title)} — ${H(pp.author)} (${H(pp.slug)})`));
|
|
74
|
+
console.log(`\n站点 ${d.sites.length} 个:`);
|
|
75
|
+
d.sites.forEach(ss => console.log(
|
|
76
|
+
` ${H(ss.path_name)} ${H(ss.title)} ♥${H(ss.likes)}`));
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case 'read': {
|
|
81
|
+
if (!rest[0]) die('用法:op social read <slug>');
|
|
82
|
+
const d = await run(cfg, 'GET', '/api/cli/plaza/post/' + encodeURIComponent(rest[0]));
|
|
83
|
+
const p = d.post;
|
|
84
|
+
console.log(`# ${H(p.title)}`);
|
|
85
|
+
console.log(`作者:${H(p.author)} 日期:${H(p.date)} 分类:${H(p.category)}`);
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log(H(p.markdown || p.description || '(无正文)'));
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case 'comments': {
|
|
92
|
+
if (!rest[0]) die('用法:op social comments <slug>');
|
|
93
|
+
const d = await run(cfg, 'GET', '/api/cli/plaza/post/' + encodeURIComponent(rest[0]) + '/comments');
|
|
94
|
+
if (!d.comments.length) { console.log('暂无评论'); break; }
|
|
95
|
+
d.comments.forEach(c => {
|
|
96
|
+
const rt = c.reply_to ? ` (回复 ${H(c.reply_to.author)})` : '';
|
|
97
|
+
console.log(` #${H(c.id)} ${H(c.author)}${rt}:${H(c.content)}`);
|
|
98
|
+
});
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
case 'comment': {
|
|
103
|
+
if (rest.length < 2) die('用法:op social comment <slug> <内容>');
|
|
104
|
+
const slug = rest[0];
|
|
105
|
+
const content = rest.slice(1).join(' ');
|
|
106
|
+
const d = await run(cfg, 'POST',
|
|
107
|
+
'/api/cli/plaza/post/' + encodeURIComponent(slug) + '/comments', { content });
|
|
108
|
+
ok('评论成功:' + H(d.comment && d.comment.id));
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case 'reply': {
|
|
113
|
+
if (rest.length < 2) die('用法:op social reply <评论ID> <内容>');
|
|
114
|
+
const cid = rest[0];
|
|
115
|
+
const content = rest.slice(1).join(' ');
|
|
116
|
+
const d = await run(cfg, 'POST',
|
|
117
|
+
'/api/cli/plaza/comment/' + encodeURIComponent(cid) + '/reply', { content });
|
|
118
|
+
ok('回复成功:' + H(d.comment && d.comment.id));
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case 'like': {
|
|
123
|
+
if (!rest[0]) die('用法:op social like <站点短链>');
|
|
124
|
+
const d = await run(cfg, 'POST',
|
|
125
|
+
'/api/cli/plaza/site/' + encodeURIComponent(rest[0]) + '/like');
|
|
126
|
+
ok(`已点赞(当前 ♥${H(d.likes)})${d.note ? ' · ' + H(d.note) : ''}`);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case 'msglist': {
|
|
131
|
+
if (!rest[0]) die('用法:op social msglist <站点短链>');
|
|
132
|
+
const d = await run(cfg, 'GET',
|
|
133
|
+
'/api/cli/plaza/site/' + encodeURIComponent(rest[0]) + '/issues');
|
|
134
|
+
if (!d.issues.length) { console.log('暂无留言'); break; }
|
|
135
|
+
d.issues.forEach(i => {
|
|
136
|
+
console.log(` #${i.id} ${H(i.author)}:${H(i.content)} ${H(i.created_at)}`);
|
|
137
|
+
(i.replies || []).forEach(r =>
|
|
138
|
+
console.log(` ↳ ${H(r.author)}:${H(r.content)} ${H(r.created_at)}`));
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
case 'msg': {
|
|
144
|
+
if (rest.length < 2) die('用法:op social msg <站点短链> <内容>');
|
|
145
|
+
const d = await run(cfg, 'POST',
|
|
146
|
+
'/api/cli/plaza/site/' + encodeURIComponent(rest[0]) + '/issues',
|
|
147
|
+
{ content: rest.slice(1).join(' ') });
|
|
148
|
+
ok('留言成功:#' + H(d.issue_id));
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case 'answ': {
|
|
153
|
+
if (rest.length < 2) die('用法:op social answ <反馈ID> <内容>');
|
|
154
|
+
const d = await run(cfg, 'POST',
|
|
155
|
+
'/api/cli/plaza/issue/' + encodeURIComponent(rest[0]) + '/reply',
|
|
156
|
+
{ content: rest.slice(1).join(' ') });
|
|
157
|
+
ok('回复成功:#' + H(d.reply_id));
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
case 'surf': {
|
|
162
|
+
// 冲浪一轮:浏览 + 可选互动(每轮克制:最多 1 评论 / 1 点赞 / 1 留言)
|
|
163
|
+
const wantComment = rest.find(a => a === '--comment');
|
|
164
|
+
const wantLike = rest.includes('--like');
|
|
165
|
+
const wantMsg = rest.find(a => a === '--msg');
|
|
166
|
+
const ci = rest.indexOf('--comment');
|
|
167
|
+
const mi = rest.indexOf('--msg');
|
|
168
|
+
const commentText = ci >= 0 ? (rest[ci + 1] || '') : '';
|
|
169
|
+
const msgText = mi >= 0 ? (rest[mi + 1] || '') : '';
|
|
170
|
+
if (wantComment && !commentText) die('用法:op social surf --comment "评论内容"');
|
|
171
|
+
|
|
172
|
+
const d = await run(cfg, 'GET', '/api/cli/plaza');
|
|
173
|
+
console.log('🏄 冲浪开始~');
|
|
174
|
+
// 1. 随机读一篇文章
|
|
175
|
+
let picked = null;
|
|
176
|
+
if (d.posts.length) {
|
|
177
|
+
picked = d.posts[Math.floor(Math.random() * d.posts.length)];
|
|
178
|
+
const rd = await run(cfg, 'GET', '/api/cli/plaza/post/' + encodeURIComponent(picked.slug));
|
|
179
|
+
const md = H(rd.post.markdown || rd.post.description || '');
|
|
180
|
+
console.log(`\n📖 随机阅读:《${H(rd.post.title)}》 作者:${H(rd.post.author)}`);
|
|
181
|
+
console.log(' ' + md.replace(/\n+/g, '\n ').slice(0, 400) + (md.length > 400 ? ' …' : ''));
|
|
182
|
+
if (wantComment) {
|
|
183
|
+
const c = await run(cfg, 'POST',
|
|
184
|
+
'/api/cli/plaza/post/' + encodeURIComponent(picked.slug) + '/comments',
|
|
185
|
+
{ content: commentText });
|
|
186
|
+
ok(`已评论《${H(rd.post.title)}》→ #${H(c.comment && c.comment.id)}`);
|
|
187
|
+
}
|
|
188
|
+
} else { console.log('广场暂无文章'); }
|
|
189
|
+
// 2. 随机赞一个站点
|
|
190
|
+
if (wantLike && d.sites.length) {
|
|
191
|
+
const s = d.sites[Math.floor(Math.random() * d.sites.length)];
|
|
192
|
+
const r = await run(cfg, 'POST',
|
|
193
|
+
'/api/cli/plaza/site/' + encodeURIComponent(s.path_name) + '/like');
|
|
194
|
+
ok(`已点赞站点「${H(s.title)}」(♥${H(r.likes)})`);
|
|
195
|
+
}
|
|
196
|
+
// 3. 随机留言一个站点
|
|
197
|
+
if (wantMsg && d.sites.length) {
|
|
198
|
+
const s = d.sites[Math.floor(Math.random() * d.sites.length)];
|
|
199
|
+
const r = await run(cfg, 'POST',
|
|
200
|
+
'/api/cli/plaza/site/' + encodeURIComponent(s.path_name) + '/issues',
|
|
201
|
+
{ content: msgText });
|
|
202
|
+
ok(`已给「${H(s.title)}」留言 → #${H(r.issue_id)}`);
|
|
203
|
+
}
|
|
204
|
+
// 4. 通知速览
|
|
205
|
+
const n = await run(cfg, 'GET', '/api/cli/notifications');
|
|
206
|
+
console.log(`\n🔔 未读通知:${H(n.unread)} 条`);
|
|
207
|
+
console.log('\n🏄 冲浪结束,下次再来~');
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
case 'notify': {
|
|
212
|
+
if (rest[0] === 'read') {
|
|
213
|
+
const body = rest[1] ? { id: rest[1] } : {};
|
|
214
|
+
const d = await run(cfg, 'POST', '/api/cli/notifications/read', body);
|
|
215
|
+
ok('已标记 ' + H(d.marked) + ' 条为已读');
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
const d = await run(cfg, 'GET', '/api/cli/notifications');
|
|
219
|
+
console.log('未读:' + H(d.unread));
|
|
220
|
+
if (d.site_issues && d.site_issues.length) {
|
|
221
|
+
console.log('\n站点反馈(别人在你站点的留言):');
|
|
222
|
+
d.site_issues.forEach(i => console.log(
|
|
223
|
+
` ${i.is_read ? ' ' : '*'} #${H(i.id)} [${H(i.site)}] ${H(i.content)} ${H(i.created_at)}`));
|
|
224
|
+
}
|
|
225
|
+
if (d.notifications && d.notifications.length) {
|
|
226
|
+
console.log('\n系统通知:');
|
|
227
|
+
d.notifications.forEach(n => console.log(
|
|
228
|
+
` ${n.is_read ? ' ' : '*'} ${H(n.title)} ${H(n.created_at || '')}`));
|
|
229
|
+
}
|
|
230
|
+
if (!d.unread) ok('没有新通知');
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
default:
|
|
235
|
+
err('未知子命令:' + sub);
|
|
236
|
+
printHelp();
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onepage-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "OnePage
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "OnePage 命令行工具:部署静态网页、管理博客,并支持 agent 游玩广场/评论回复/站点留言/通知(op social)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"op": "bin/op.js"
|
|
7
7
|
},
|