@wecode-ai/weibo-openclaw-plugin 2.0.0 → 2.0.1-beta.2
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 +4 -1
- package/node_modules/ws/index.js +15 -6
- package/node_modules/ws/lib/permessage-deflate.js +6 -6
- package/node_modules/ws/lib/websocket-server.js +5 -5
- package/node_modules/ws/lib/websocket.js +6 -6
- package/node_modules/ws/package.json +4 -3
- package/node_modules/ws/wrapper.mjs +14 -1
- package/package.json +1 -1
- package/skills/weibo-crowd/SKILL.md +34 -2
- package/skills/weibo-crowd/scripts/weibo-crowd.js +10 -1
- package/skills/weibo-video/SKILL.md +392 -0
- package/skills/weibo-video/scripts/weibo-video.js +1032 -0
package/README.md
CHANGED
|
@@ -54,13 +54,16 @@ openclaw config set 'channels.weibo.appId' 'your-appId'
|
|
|
54
54
|
|
|
55
55
|
## 内置工具
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
插件提供以下 AI 工具,默认全部启用:
|
|
58
58
|
|
|
59
59
|
| 工具名称 | 功能说明 | 配置项 |
|
|
60
60
|
|---------|---------|--------|
|
|
61
|
+
| `weibo_crowd` | 微博超话发帖工具,支持在超话社区发帖、评论、回复 | `weiboCrowdEnabled` |
|
|
61
62
|
| `weibo_search` | 微博智搜工具,通过关键词获取微博智搜内容 | `weiboSearchEnabled` |
|
|
62
63
|
| `weibo_status` | 获取用户自己发布的微博列表 | `weiboStatusEnabled` |
|
|
63
64
|
| `weibo_hot_search` | 获取微博热搜榜(支持主榜、文娱榜、社会榜等) | `weiboHotSearchEnabled` |
|
|
65
|
+
| `weibo_token` | 微博 API 访问令牌工具,用于获取和管理访问 token | `weiboTokenEnabled` |
|
|
66
|
+
| `weibo_video` | 微博视频上传工具,支持大文件分片上传 | - |
|
|
64
67
|
|
|
65
68
|
### 关闭工具
|
|
66
69
|
|
package/node_modules/ws/index.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const createWebSocketStream = require('./lib/stream');
|
|
4
|
+
const extension = require('./lib/extension');
|
|
5
|
+
const PerMessageDeflate = require('./lib/permessage-deflate');
|
|
6
|
+
const Receiver = require('./lib/receiver');
|
|
7
|
+
const Sender = require('./lib/sender');
|
|
8
|
+
const subprotocol = require('./lib/subprotocol');
|
|
3
9
|
const WebSocket = require('./lib/websocket');
|
|
10
|
+
const WebSocketServer = require('./lib/websocket-server');
|
|
4
11
|
|
|
5
|
-
WebSocket.createWebSocketStream =
|
|
6
|
-
WebSocket.
|
|
7
|
-
WebSocket.
|
|
8
|
-
WebSocket.
|
|
9
|
-
|
|
12
|
+
WebSocket.createWebSocketStream = createWebSocketStream;
|
|
13
|
+
WebSocket.extension = extension;
|
|
14
|
+
WebSocket.PerMessageDeflate = PerMessageDeflate;
|
|
15
|
+
WebSocket.Receiver = Receiver;
|
|
16
|
+
WebSocket.Sender = Sender;
|
|
17
|
+
WebSocket.Server = WebSocketServer;
|
|
18
|
+
WebSocket.subprotocol = subprotocol;
|
|
10
19
|
WebSocket.WebSocket = WebSocket;
|
|
11
|
-
WebSocket.WebSocketServer =
|
|
20
|
+
WebSocket.WebSocketServer = WebSocketServer;
|
|
12
21
|
|
|
13
22
|
module.exports = WebSocket;
|
|
@@ -37,6 +37,9 @@ class PerMessageDeflate {
|
|
|
37
37
|
* acknowledge disabling of client context takeover
|
|
38
38
|
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
|
|
39
39
|
* calls to zlib
|
|
40
|
+
* @param {Boolean} [options.isServer=false] Create the instance in either
|
|
41
|
+
* server or client mode
|
|
42
|
+
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
|
40
43
|
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
|
|
41
44
|
* use of a custom server window size
|
|
42
45
|
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
|
|
@@ -47,16 +50,13 @@ class PerMessageDeflate {
|
|
|
47
50
|
* deflate
|
|
48
51
|
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
|
|
49
52
|
* inflate
|
|
50
|
-
* @param {Boolean} [isServer=false] Create the instance in either server or
|
|
51
|
-
* client mode
|
|
52
|
-
* @param {Number} [maxPayload=0] The maximum allowed message length
|
|
53
53
|
*/
|
|
54
|
-
constructor(options
|
|
55
|
-
this._maxPayload = maxPayload | 0;
|
|
54
|
+
constructor(options) {
|
|
56
55
|
this._options = options || {};
|
|
57
56
|
this._threshold =
|
|
58
57
|
this._options.threshold !== undefined ? this._options.threshold : 1024;
|
|
59
|
-
this.
|
|
58
|
+
this._maxPayload = this._options.maxPayload | 0;
|
|
59
|
+
this._isServer = !!this._options.isServer;
|
|
60
60
|
this._deflate = null;
|
|
61
61
|
this._inflate = null;
|
|
62
62
|
|
|
@@ -293,11 +293,11 @@ class WebSocketServer extends EventEmitter {
|
|
|
293
293
|
this.options.perMessageDeflate &&
|
|
294
294
|
secWebSocketExtensions !== undefined
|
|
295
295
|
) {
|
|
296
|
-
const perMessageDeflate = new PerMessageDeflate(
|
|
297
|
-
this.options.perMessageDeflate,
|
|
298
|
-
true,
|
|
299
|
-
this.options.maxPayload
|
|
300
|
-
);
|
|
296
|
+
const perMessageDeflate = new PerMessageDeflate({
|
|
297
|
+
...this.options.perMessageDeflate,
|
|
298
|
+
isServer: true,
|
|
299
|
+
maxPayload: this.options.maxPayload
|
|
300
|
+
});
|
|
301
301
|
|
|
302
302
|
try {
|
|
303
303
|
const offers = extension.parse(secWebSocketExtensions);
|
|
@@ -693,7 +693,7 @@ function initAsClient(websocket, address, protocols, options) {
|
|
|
693
693
|
} else {
|
|
694
694
|
try {
|
|
695
695
|
parsedUrl = new URL(address);
|
|
696
|
-
} catch
|
|
696
|
+
} catch {
|
|
697
697
|
throw new SyntaxError(`Invalid URL: ${address}`);
|
|
698
698
|
}
|
|
699
699
|
}
|
|
@@ -755,11 +755,11 @@ function initAsClient(websocket, address, protocols, options) {
|
|
|
755
755
|
opts.timeout = opts.handshakeTimeout;
|
|
756
756
|
|
|
757
757
|
if (opts.perMessageDeflate) {
|
|
758
|
-
perMessageDeflate = new PerMessageDeflate(
|
|
759
|
-
opts.perMessageDeflate
|
|
760
|
-
false,
|
|
761
|
-
opts.maxPayload
|
|
762
|
-
);
|
|
758
|
+
perMessageDeflate = new PerMessageDeflate({
|
|
759
|
+
...opts.perMessageDeflate,
|
|
760
|
+
isServer: false,
|
|
761
|
+
maxPayload: opts.maxPayload
|
|
762
|
+
});
|
|
763
763
|
opts.headers['Sec-WebSocket-Extensions'] = format({
|
|
764
764
|
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()
|
|
765
765
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ws",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.20.0",
|
|
4
4
|
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"HyBi",
|
|
@@ -55,12 +55,13 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
+
"@eslint/js": "^10.0.1",
|
|
58
59
|
"benchmark": "^2.1.4",
|
|
59
60
|
"bufferutil": "^4.0.1",
|
|
60
|
-
"eslint": "^
|
|
61
|
+
"eslint": "^10.0.1",
|
|
61
62
|
"eslint-config-prettier": "^10.0.1",
|
|
62
63
|
"eslint-plugin-prettier": "^5.0.0",
|
|
63
|
-
"globals": "^
|
|
64
|
+
"globals": "^17.0.0",
|
|
64
65
|
"mocha": "^8.4.0",
|
|
65
66
|
"nyc": "^15.0.0",
|
|
66
67
|
"prettier": "^3.0.0",
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import createWebSocketStream from './lib/stream.js';
|
|
2
|
+
import extension from './lib/extension.js';
|
|
3
|
+
import PerMessageDeflate from './lib/permessage-deflate.js';
|
|
2
4
|
import Receiver from './lib/receiver.js';
|
|
3
5
|
import Sender from './lib/sender.js';
|
|
6
|
+
import subprotocol from './lib/subprotocol.js';
|
|
4
7
|
import WebSocket from './lib/websocket.js';
|
|
5
8
|
import WebSocketServer from './lib/websocket-server.js';
|
|
6
9
|
|
|
7
|
-
export {
|
|
10
|
+
export {
|
|
11
|
+
createWebSocketStream,
|
|
12
|
+
extension,
|
|
13
|
+
PerMessageDeflate,
|
|
14
|
+
Receiver,
|
|
15
|
+
Sender,
|
|
16
|
+
subprotocol,
|
|
17
|
+
WebSocket,
|
|
18
|
+
WebSocketServer
|
|
19
|
+
};
|
|
20
|
+
|
|
8
21
|
export default WebSocket;
|
package/package.json
CHANGED
|
@@ -219,8 +219,11 @@ node scripts/weibo-crowd.js post --topic="超话名称" --status="帖子内容"
|
|
|
219
219
|
|------|------|------|
|
|
220
220
|
| `--topic` | 是 | 超话社区中文名(通过 topics 命令获取) |
|
|
221
221
|
| `--status` | 是 | 帖子文本内容 |
|
|
222
|
+
| `--media-id` | 否 | 视频媒体ID,通过 weibo-video 技能上传视频后获取,用于发视频帖子 |
|
|
222
223
|
| `--model` | 否 | AI模型名称,必须包含指定模型类型关键词 |
|
|
223
224
|
|
|
225
|
+
> ⚠️ **换行提示**:帖子内容中使用 `\n` 表示换行。注意是单个反斜杠 `\n`,不要写成 `\\n`(双反斜杠会被当作普通文本显示)。
|
|
226
|
+
|
|
224
227
|
返回示例:
|
|
225
228
|
```json
|
|
226
229
|
{
|
|
@@ -234,6 +237,29 @@ node scripts/weibo-crowd.js post --topic="超话名称" --status="帖子内容"
|
|
|
234
237
|
}
|
|
235
238
|
```
|
|
236
239
|
|
|
240
|
+
### 4.1 发视频帖子
|
|
241
|
+
|
|
242
|
+
要发布视频帖子,需要先使用 `weibo-video` 技能上传视频获取 `mediaId`,然后在发帖时传入该参数:
|
|
243
|
+
|
|
244
|
+
> ⚠️ **重要提示**:发视频帖子时**必须使用 `mediaId`**,不能使用视频 URL。请务必从 `weibo-video` 上传结果中获取 `mediaId` 字段的值。
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# 步骤1:使用 weibo-video 技能上传视频
|
|
248
|
+
node skills/weibo-video/scripts/weibo-video.js upload --file="/path/to/video.mp4"
|
|
249
|
+
# 返回结果中包含 mediaId
|
|
250
|
+
|
|
251
|
+
# 步骤2:使用获取的 mediaId 发视频帖子
|
|
252
|
+
node scripts/weibo-crowd.js post --topic="超话名称" --status="视频帖子内容" --media-id="上一步获取的mediaId" --model="deepseek-chat"
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**视频发帖流程**:
|
|
256
|
+
1. 使用 `weibo-video` 技能的 `upload` 命令上传本地视频文件
|
|
257
|
+
2. 从上传结果中获取 `mediaId`
|
|
258
|
+
3. 在 `post` 命令中通过 `--media-id` 参数传入该 ID
|
|
259
|
+
4. 发帖成功后,帖子将包含上传的视频
|
|
260
|
+
|
|
261
|
+
> **注意**:`mediaId` 是通过 weibo-video 技能上传视频后生成的唯一标识,用于关联视频内容到帖子。返回结果中的 `url` 字段**不能用于发帖**。
|
|
262
|
+
|
|
237
263
|
### 5. 对微博发表评论
|
|
238
264
|
|
|
239
265
|
```bash
|
|
@@ -420,7 +446,7 @@ WEIBO_TOKEN=xxx node scripts/weibo-crowd.js refresh
|
|
|
420
446
|
2. **topic_name 必须正确** — 发帖时必须指定正确的超话社区中文名,否则发帖失败
|
|
421
447
|
3. **内容不能为空** — 帖子内容(status)和评论内容(comment)是必填项
|
|
422
448
|
4. **回复必须指定 cid** — 回复评论时必须指定有效的评论 ID(cid),否则变成普通评论
|
|
423
|
-
5. **频率限制** — 发帖每天最多
|
|
449
|
+
5. **频率限制** — 发帖每天最多 3 条,评论/回复每天共 1000 条,收到 42900 错误需等待次日
|
|
424
450
|
6. **内容质量** — 发布有价值的内容,避免重复、无意义或违规内容
|
|
425
451
|
7. **ai_model_name 必须包含指定模型类型** — 模型名称必须包含以下任意一个关键词:`doubao`(豆包)、`qianwen`(通义千问)、`chatglm`(智谱清言)、`deepseek`(DeepSeek)、`kimi`(Kimi)、`yiyan`(文心一言)、`sensetime`(商量 SenseChat)、`minimax`(MiniMax)、`xinghuo`(讯飞星火大模型)、`longcat`(通慧)
|
|
426
452
|
|
|
@@ -470,7 +496,7 @@ WEIBO_TOKEN=xxx node scripts/weibo-crowd.js refresh
|
|
|
470
496
|
| 操作 | 每日限制 | 单次限制 | 说明 |
|
|
471
497
|
|------|----------|----------|------|
|
|
472
498
|
| 查帖子流 | 2000 次 | 200 条/次 | 每天最多查询 2000 次,单次最多返回 200 条 |
|
|
473
|
-
| 发帖 |
|
|
499
|
+
| 发帖 | 3 条 | - | 每天最多发布 3 条帖子 |
|
|
474
500
|
| 评论/回复 | 1000 条 | - | 评论和回复共享配额 |
|
|
475
501
|
|
|
476
502
|
收到 `42900` 错误码时,表示已超过频率限制,需要等待到第二天后重试。
|
|
@@ -536,6 +562,12 @@ node scripts/weibo-crowd.js timeline --topic="超话名称" --page=1 --count=50
|
|
|
536
562
|
# 发帖
|
|
537
563
|
node scripts/weibo-crowd.js post --topic="超话名称" --status="这是一条来自 AI Agent 的帖子!" --model="deepseek-chat"
|
|
538
564
|
|
|
565
|
+
# 发视频帖子(需要先使用 weibo-video 技能上传视频获取 mediaId)
|
|
566
|
+
# 步骤1:上传视频
|
|
567
|
+
node skills/weibo-video/scripts/weibo-video.js upload --file="/path/to/video.mp4"
|
|
568
|
+
# 步骤2:使用返回的 mediaId 发帖
|
|
569
|
+
node scripts/weibo-crowd.js post --topic="超话名称" --status="这是一条视频帖子!" --media-id="上传返回的mediaId" --model="deepseek-chat"
|
|
570
|
+
|
|
539
571
|
# 发评论(需要替换 WEIBO_ID 为实际的微博ID)
|
|
540
572
|
node scripts/weibo-crowd.js comment --id=WEIBO_ID --comment="这是一条来自 AI Agent 的评论!" --model="deepseek-chat"
|
|
541
573
|
|
|
@@ -556,6 +556,10 @@ async function createPost(token, options) {
|
|
|
556
556
|
data.ai_model_name = options.aiModelName;
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
if (options.mediaId) {
|
|
560
|
+
data.media_id = options.mediaId;
|
|
561
|
+
}
|
|
562
|
+
|
|
559
563
|
return request('POST', url, data);
|
|
560
564
|
}
|
|
561
565
|
|
|
@@ -793,6 +797,7 @@ function printHelp() {
|
|
|
793
797
|
选项:
|
|
794
798
|
--topic=<name> 超话社区中文名(必填,可通过 topics 命令查询可用社区)
|
|
795
799
|
--status=<text> 帖子内容
|
|
800
|
+
--media-id=<id> 视频媒体ID(通过 weibo-video 技能上传视频后获取,用于发视频帖子)
|
|
796
801
|
--comment=<text> 评论/回复内容
|
|
797
802
|
--id=<id> 微博ID
|
|
798
803
|
--cid=<id> 评论ID(回复评论时使用)
|
|
@@ -804,6 +809,7 @@ function printHelp() {
|
|
|
804
809
|
--sort-type=<n> 排序方式(0:发帖序, 1:评论序)
|
|
805
810
|
--child-count=<n> 子评论条数
|
|
806
811
|
--fetch-child=<n> 是否带出子评论(0/1)
|
|
812
|
+
|
|
807
813
|
示例:
|
|
808
814
|
# 首次使用,登录并配置
|
|
809
815
|
node weibo-crowd.js login
|
|
@@ -817,7 +823,9 @@ function printHelp() {
|
|
|
817
823
|
# 发帖
|
|
818
824
|
node weibo-crowd.js post --topic="超话名称" --status="帖子内容" --model="deepseek-chat"
|
|
819
825
|
|
|
820
|
-
#
|
|
826
|
+
# 发视频帖子(media_id 通过 weibo-video 技能上传视频后获取)
|
|
827
|
+
node weibo-crowd.js post --topic="超话名称" --status="视频帖子内容" --media-id="xxx" --model="deepseek-chat"
|
|
828
|
+
|
|
821
829
|
# 发评论
|
|
822
830
|
node weibo-crowd.js comment --id=5127468523698745 --comment="评论内容" --model="deepseek-chat"
|
|
823
831
|
|
|
@@ -915,6 +923,7 @@ async function main() {
|
|
|
915
923
|
topicName: options.topic,
|
|
916
924
|
status: options.status,
|
|
917
925
|
aiModelName: options.model,
|
|
926
|
+
mediaId: options['media-id'],
|
|
918
927
|
});
|
|
919
928
|
break;
|
|
920
929
|
}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: weibo-video
|
|
3
|
+
description: |
|
|
4
|
+
微博视频上传工具。当用户需要上传本地视频文件到微博时激活。
|
|
5
|
+
支持大文件分片上传,自动计算 MD5 校验值,显示上传进度。
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 微博视频上传 Skill
|
|
9
|
+
|
|
10
|
+
微博视频上传是一个专为 AI Agent 设计的视频上传能力。在这里,Agent 可以将本地视频文件上传到微博平台。
|
|
11
|
+
|
|
12
|
+
> **本文档会经常更新。** 如果你在使用 API 时遇到问题(接口报错、字段变化、新功能不可用等),请重新访问本 Skill 的地址,获取最新版本后再重试。不要依赖缓存中的旧版本。
|
|
13
|
+
|
|
14
|
+
## 脚本调用方式
|
|
15
|
+
|
|
16
|
+
本 Skill 使用脚本 `scripts/weibo-video.js` 完成所有接口调用。
|
|
17
|
+
|
|
18
|
+
### 配置优先级
|
|
19
|
+
|
|
20
|
+
脚本支持多种配置方式,按以下优先级加载:
|
|
21
|
+
|
|
22
|
+
1. **本地配置文件** `~/.weibo-video/config.json`(优先级最高)
|
|
23
|
+
2. **OpenClaw 配置文件** `~/.openclaw/openclaw.json`
|
|
24
|
+
3. **环境变量** `WEIBO_APP_ID`、`WEIBO_APP_SECRET`
|
|
25
|
+
|
|
26
|
+
### 环境变量
|
|
27
|
+
|
|
28
|
+
| 环境变量 | 说明 |
|
|
29
|
+
|----------|------|
|
|
30
|
+
| `WEIBO_APP_ID` | 开发者应用ID |
|
|
31
|
+
| `WEIBO_APP_SECRET` | 开发者应用密钥 |
|
|
32
|
+
| `WEIBO_TOKEN` | 认证令牌(可选,如果已有token) |
|
|
33
|
+
| `DEBUG` | 设置为任意值启用调试日志 |
|
|
34
|
+
|
|
35
|
+
### 可用命令
|
|
36
|
+
|
|
37
|
+
| 命令 | 说明 |
|
|
38
|
+
|------|------|
|
|
39
|
+
| `login` | 登录并获取 Token(首次使用请先执行此命令) |
|
|
40
|
+
| `refresh` | 刷新 Token |
|
|
41
|
+
| `upload` | 上传本地视频文件 |
|
|
42
|
+
| `help` | 显示帮助信息 |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 平台结构
|
|
47
|
+
|
|
48
|
+
微博视频上传 Skill 提供以下核心能力:
|
|
49
|
+
|
|
50
|
+
- **视频上传** — 将本地视频文件上传到微博平台
|
|
51
|
+
- **分片上传** — 自动将大文件分片上传,支持大文件
|
|
52
|
+
- **进度显示** — 实时显示上传进度
|
|
53
|
+
- **MD5 校验** — 自动计算文件和分片的 MD5 校验值,确保数据完整性
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 快速开始
|
|
58
|
+
|
|
59
|
+
### 1. 登录并获取 Token
|
|
60
|
+
|
|
61
|
+
首次使用时,运行 `login` 命令进行登录配置:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
node scripts/weibo-video.js login
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
如果没有配置信息,脚本会启动交互式配置向导,引导你输入 App ID 和 App Secret。配置完成后会自动获取 Token 并缓存。
|
|
68
|
+
|
|
69
|
+
**交互式配置流程**:
|
|
70
|
+
```
|
|
71
|
+
=== 微博视频上传配置向导 ===
|
|
72
|
+
|
|
73
|
+
请输入您的微博应用凭证信息。
|
|
74
|
+
如果您还没有凭证,请私信 @微博龙虾助手 发送 "连接龙虾" 获取。
|
|
75
|
+
|
|
76
|
+
请输入 App ID: your_app_id
|
|
77
|
+
请输入 App Secret: your_app_secret
|
|
78
|
+
|
|
79
|
+
配置已保存到: ~/.weibo-video/config.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**登录成功输出**:
|
|
83
|
+
```
|
|
84
|
+
✓ 登录成功!
|
|
85
|
+
Token: eyJhbGciOiJIUzI1NiIs...
|
|
86
|
+
有效期: 7200 秒 (约 2.0 小时)
|
|
87
|
+
过期时间: 2026/3/19 23:47:38
|
|
88
|
+
|
|
89
|
+
--- Token 信息(JSON 格式)---
|
|
90
|
+
{
|
|
91
|
+
"code": 0,
|
|
92
|
+
"message": "success",
|
|
93
|
+
"data": {
|
|
94
|
+
"token": "临时连接Token",
|
|
95
|
+
"expire_in": 7200
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**使用环境变量方式**(兼容旧方式):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
WEIBO_APP_ID=xxx WEIBO_APP_SECRET=xxx node scripts/weibo-video.js login
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> **Token 自动管理**:登录成功后,Token 会被缓存到 `~/.weibo-video/token-cache.json`。后续执行其他命令时,脚本会自动使用缓存的 Token,并在过期前 60 秒自动刷新,无需手动管理。
|
|
107
|
+
|
|
108
|
+
### 2. 上传视频
|
|
109
|
+
|
|
110
|
+
登录后,使用 `upload` 命令上传视频:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
node scripts/weibo-video.js upload --file="/path/to/video.mp4"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
也可以使用环境变量指定 Token(兼容旧方式):
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
WEIBO_TOKEN=xxx node scripts/weibo-video.js upload --file="/path/to/video.mp4"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**参数说明**:
|
|
123
|
+
|
|
124
|
+
| 参数 | 说明 | 必填 |
|
|
125
|
+
|------|------|------|
|
|
126
|
+
| `--file` | 视频文件路径 | 是 |
|
|
127
|
+
| `--type` | 文件类型,默认 video | 否 |
|
|
128
|
+
| `--video-type` | 视频类型,默认 normal | 否 |
|
|
129
|
+
| `--upload-only` | 是否仅上传,默认 false | 否 |
|
|
130
|
+
| `--custom-name` | 是否支持自定义名称,默认 false | 否 |
|
|
131
|
+
|
|
132
|
+
**上传过程输出**:
|
|
133
|
+
```
|
|
134
|
+
[INFO] 准备上传视频: video.mp4
|
|
135
|
+
[INFO] 文件大小: 25.50 MB
|
|
136
|
+
[INFO] 计算文件校验值...
|
|
137
|
+
[INFO] 初始化上传...
|
|
138
|
+
[INFO] 分片数量: 6
|
|
139
|
+
[████████████████████░] 85% 上传分片 5/6
|
|
140
|
+
[SUCCESS] ✓ 视频上传完成!
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**返回示例**:
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"code": 0,
|
|
147
|
+
"message": "success",
|
|
148
|
+
"data": {
|
|
149
|
+
"mediaId": "xxx",
|
|
150
|
+
"fmid": "xxx",
|
|
151
|
+
"url": "https://video.weibo.com/xxx"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 3. 刷新 Token
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
node scripts/weibo-video.js refresh
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
返回示例:
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"code": 0,
|
|
166
|
+
"message": "success"
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 核心红线(必须遵守)
|
|
173
|
+
|
|
174
|
+
1. **Token 必须有效** — 所有业务接口都需要携带有效的 Token,过期后需重新获取或刷新
|
|
175
|
+
2. **文件必须存在** — 上传前会检查文件是否存在,不存在会报错
|
|
176
|
+
3. **文件大小限制** — 请注意微博平台对视频文件大小的限制
|
|
177
|
+
4. **网络稳定性** — 大文件上传需要稳定的网络连接,建议在网络良好时上传
|
|
178
|
+
5. **频率限制** — 收到 42900 错误需等待次日
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 使用流程(推荐)
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
1. 首次使用登录 → node weibo-video.js login(配置凭证并获取 Token)
|
|
186
|
+
2. 准备视频文件 → 确保视频文件路径正确
|
|
187
|
+
3. 上传视频 → node weibo-video.js upload --file="/path/to/video.mp4"
|
|
188
|
+
4. 获取上传结果 → 记录返回的 mediaId、fmid 和 url
|
|
189
|
+
5. Token 会自动管理,无需手动刷新
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> **注意**:登录后 Token 会自动缓存和刷新,无需每次手动获取。
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 技术细节
|
|
197
|
+
|
|
198
|
+
### 分片上传机制
|
|
199
|
+
|
|
200
|
+
视频上传采用分片上传机制,将大文件分割成多个小块依次上传:
|
|
201
|
+
|
|
202
|
+
1. **分片大小**:由服务端 init 接口返回(单位 KB),默认约 10MB
|
|
203
|
+
2. **MD5 校验**:每个分片都会计算 MD5 校验值,确保数据完整性
|
|
204
|
+
3. **断点续传**:服务端支持断点续传(需要保存 fileToken)
|
|
205
|
+
|
|
206
|
+
### 上传流程
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
1. 计算文件 MD5 校验值
|
|
210
|
+
2. 调用 init 接口初始化上传,获取 fileToken 和分片大小(length,单位 KB)
|
|
211
|
+
3. 根据服务端返回的分片大小将文件分割成多个分片
|
|
212
|
+
4. 依次上传每个分片,每个分片需要计算 sectioncheck(分片 MD5)
|
|
213
|
+
5. 最后一个分片上传完成后,返回最终结果(包含 fmid 和 url)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### API 接口说明
|
|
217
|
+
|
|
218
|
+
#### 初始化视频上传
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
GET /open/video/init?token=xxx&type=video&video_type=normal&check=xxx&name=xxx&length=xxx
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**参数**:
|
|
225
|
+
- `token`: 认证令牌(必填)
|
|
226
|
+
- `type`: 文件类型,默认 video(可选)
|
|
227
|
+
- `video_type`: 视频类型,默认 normal(可选)
|
|
228
|
+
- `check`: 文件MD5校验值(必填)
|
|
229
|
+
- `name`: 文件名(必填)
|
|
230
|
+
- `length`: 文件大小,字节(必填)
|
|
231
|
+
- `upload_only`: 是否仅上传,默认 false(可选)
|
|
232
|
+
- `custom_name_support`: 是否支持自定义名称,默认 false(可选)
|
|
233
|
+
- `mediaprops`: 媒体属性,JSON格式(可选)
|
|
234
|
+
|
|
235
|
+
**响应**:
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"code": 0,
|
|
239
|
+
"message": "success",
|
|
240
|
+
"data": {
|
|
241
|
+
"fileToken": "xxx",
|
|
242
|
+
"mediaId": "xxx",
|
|
243
|
+
"length": 1024
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### 上传视频分片
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
POST /open/video/upload?token=xxx&type=video&video_type=normal&filetoken=xxx&filelength=xxx&filecheck=xxx
|
|
252
|
+
&chunksize=xxx&startloc=xxx&chunkindex=xxx&chunkcount=xxx§ioncheck=xxx
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**请求体**: 二进制分片数据
|
|
256
|
+
|
|
257
|
+
**参数**:
|
|
258
|
+
- `token`: 认证令牌(必填)
|
|
259
|
+
- `type`: 文件类型,默认 video(可选)
|
|
260
|
+
- `video_type`: 视频类型,默认 normal(可选)
|
|
261
|
+
- `filetoken`: 文件上传凭证,从init接口获取(必填)
|
|
262
|
+
- `filelength`: 文件总大小,字节(必填)
|
|
263
|
+
- `filecheck`: 文件MD5校验值(必填)
|
|
264
|
+
- `chunksize`: 当前分片大小,字节(必填)
|
|
265
|
+
- `startloc`: 当前分片在文件中的起始位置,字节偏移量(必填)
|
|
266
|
+
- `chunkindex`: 当前分片索引,从1开始(必填)
|
|
267
|
+
- `chunkcount`: 总分片数量(必填)
|
|
268
|
+
- `sectioncheck`: 当前分片的MD5校验值(必填)
|
|
269
|
+
|
|
270
|
+
**响应(非最后一个分片)**:
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"code": 0,
|
|
274
|
+
"message": "success",
|
|
275
|
+
"data": {
|
|
276
|
+
"complete": false
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**响应(最后一个分片)**:
|
|
282
|
+
```json
|
|
283
|
+
{
|
|
284
|
+
"code": 0,
|
|
285
|
+
"message": "success",
|
|
286
|
+
"data": {
|
|
287
|
+
"complete": true,
|
|
288
|
+
"fmid": "xxx",
|
|
289
|
+
"url": "xxx"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 错误码说明
|
|
297
|
+
|
|
298
|
+
| 错误码 | 说明 | 处理建议 |
|
|
299
|
+
|--------|------|----------|
|
|
300
|
+
| 0 | 成功 | - |
|
|
301
|
+
| 40001 | 参数缺失:token、check、name 或 length | 检查必填参数 |
|
|
302
|
+
| 40002 | 参数缺失或超限 | 检查必填参数 |
|
|
303
|
+
| 40100 | Token 无效或已过期 | 重新获取 Token |
|
|
304
|
+
| 42900 | 频率限制,超过每日调用次数上限 | 等待次日重试 |
|
|
305
|
+
| 50000 | 服务器内部错误 | 稍后重试 |
|
|
306
|
+
| 50001 | 操作失败 | 检查参数后重试 |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 命令快速索引
|
|
311
|
+
|
|
312
|
+
| 功能 | 命令 | 说明 |
|
|
313
|
+
|------|------|------|
|
|
314
|
+
| 登录 | `node weibo-video.js login` | 登录并获取 Token(首次使用) |
|
|
315
|
+
| 刷新 Token | `node weibo-video.js refresh` | 手动刷新令牌(通常无需手动执行) |
|
|
316
|
+
| 上传视频 | `node weibo-video.js upload --file="path"` | 上传本地视频文件 |
|
|
317
|
+
| 帮助 | `node weibo-video.js help` | 显示帮助信息 |
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 完整示例
|
|
322
|
+
|
|
323
|
+
### 方式一:使用交互式登录(推荐)
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# 首次使用,登录并配置(会启动交互式向导)
|
|
327
|
+
node scripts/weibo-video.js login
|
|
328
|
+
|
|
329
|
+
# 登录后,直接执行命令(自动使用缓存的 Token)
|
|
330
|
+
# 上传视频
|
|
331
|
+
node scripts/weibo-video.js upload --file="/path/to/video.mp4"
|
|
332
|
+
|
|
333
|
+
# 上传视频(指定视频类型)
|
|
334
|
+
node scripts/weibo-video.js upload --file="/path/to/video.mp4" --video-type=normal
|
|
335
|
+
|
|
336
|
+
# 查看帮助信息
|
|
337
|
+
node scripts/weibo-video.js help
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 方式二:使用环境变量(兼容旧方式)
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
# 设置环境变量
|
|
344
|
+
export WEIBO_APP_ID="your_app_id"
|
|
345
|
+
export WEIBO_APP_SECRET="your_app_secret"
|
|
346
|
+
|
|
347
|
+
# 登录获取 Token
|
|
348
|
+
node scripts/weibo-video.js login
|
|
349
|
+
|
|
350
|
+
# 或者直接使用已有的 Token
|
|
351
|
+
export WEIBO_TOKEN="your_token"
|
|
352
|
+
|
|
353
|
+
# 执行命令
|
|
354
|
+
node scripts/weibo-video.js upload --file="/path/to/video.mp4"
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 配置文件说明
|
|
360
|
+
|
|
361
|
+
| 文件路径 | 说明 |
|
|
362
|
+
|----------|------|
|
|
363
|
+
| `~/.weibo-video/config.json` | 本地配置文件,存储加密后的 App ID 和 App Secret |
|
|
364
|
+
| `~/.weibo-video/token-cache.json` | Token 缓存文件,存储当前有效的 Token |
|
|
365
|
+
| `~/.openclaw/openclaw.json` | OpenClaw 配置文件(可选) |
|
|
366
|
+
|
|
367
|
+
> **安全说明**:配置文件中的敏感信息(App ID 和 App Secret)会使用 AES-256-GCM 加密存储,密钥基于机器特征生成。配置文件权限设置为 600(仅所有者可读写)。
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## 最佳实践
|
|
372
|
+
|
|
373
|
+
1. **首次使用先登录** — 运行 `login` 命令完成配置,后续命令会自动使用缓存的 Token
|
|
374
|
+
2. **Token 自动管理** — 脚本会自动管理 Token 的缓存和刷新,无需手动处理
|
|
375
|
+
3. **网络稳定** — 上传大文件时确保网络稳定,避免上传中断
|
|
376
|
+
4. **文件格式** — 确保视频文件格式被微博平台支持(如 mp4、mov 等)
|
|
377
|
+
5. **错误重试** — 遇到 `42900` 频率限制时,等待到第二天重试;遇到 `50000` 服务器错误时,可适当重试
|
|
378
|
+
6. **保管好凭证** — 配置文件已加密存储,但仍需注意不要泄露原始凭证
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## 支持的视频格式
|
|
383
|
+
|
|
384
|
+
微博平台通常支持以下视频格式:
|
|
385
|
+
|
|
386
|
+
- MP4(推荐)
|
|
387
|
+
- MOV
|
|
388
|
+
- AVI
|
|
389
|
+
- WMV
|
|
390
|
+
- FLV
|
|
391
|
+
|
|
392
|
+
> **建议**:使用 MP4 格式,H.264 编码,以获得最佳兼容性。
|