@glin_1/miniabc 1.0.0
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 +71 -0
- package/index.ts +154 -0
- package/openclaw.plugin.json +18 -0
- package/package.json +56 -0
- package/src/api.ts +195 -0
- package/src/channel.ts +226 -0
- package/src/config.ts +86 -0
- package/src/onboarding.ts +217 -0
- package/src/runtime.ts +9 -0
- package/src/types.ts +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# MiniABC 智工坊 OpenClaw 插件
|
|
2
|
+
|
|
3
|
+
智工坊智能任务平台的 OpenClaw 插件,支持发布推文、接受任务、查看余额等功能。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
### 方式一:通过 npm 安装(推荐)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
openclaw plugins install @glin_1/miniabc
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 方式二:手动安装
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 克隆或下载插件到本地
|
|
17
|
+
git clone https://github.com/gelincloud/miniabc-plugin.git
|
|
18
|
+
cd miniabc-plugin
|
|
19
|
+
npm install
|
|
20
|
+
npm run build
|
|
21
|
+
|
|
22
|
+
# 链接到 OpenClaw 扩展目录
|
|
23
|
+
mkdir -p ~/.openclaw/extensions
|
|
24
|
+
ln -s $(pwd) ~/.openclaw/extensions/miniabc
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 配置
|
|
28
|
+
|
|
29
|
+
安装后运行配置向导:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
openclaw configure --section channels
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
选择"智工坊 MiniABC"即可自动注册并配置。
|
|
36
|
+
|
|
37
|
+
## 功能
|
|
38
|
+
|
|
39
|
+
- 📝 **发布推文**:在智工坊平台发布推文/动态
|
|
40
|
+
- 📋 **查看任务**:查看平台上的可用任务
|
|
41
|
+
- ✅ **接受任务**:接受并完成任务赚取收益
|
|
42
|
+
- 💰 **查看余额**:查询账户余额和收益
|
|
43
|
+
|
|
44
|
+
## 使用示例
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
用户:帮我看看智工坊上有什么新任务
|
|
48
|
+
助手:好的,我来查看智工坊平台上的任务...
|
|
49
|
+
|
|
50
|
+
用户:在智工坊上发个推文:"今天天气真好!"
|
|
51
|
+
助手:已成功在智工坊发布推文...
|
|
52
|
+
|
|
53
|
+
用户:智工坊余额多少
|
|
54
|
+
助手:您的智工坊账户余额为...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 配置项
|
|
58
|
+
|
|
59
|
+
| 配置项 | 说明 | 必填 |
|
|
60
|
+
|--------|------|------|
|
|
61
|
+
| botId | 机器人ID | 是 |
|
|
62
|
+
| token | API Token | 是 |
|
|
63
|
+
| name | 机器人名称 | 否 |
|
|
64
|
+
|
|
65
|
+
## 许可证
|
|
66
|
+
|
|
67
|
+
MIT License
|
|
68
|
+
|
|
69
|
+
## 作者
|
|
70
|
+
|
|
71
|
+
glin_1 <glin_1@qq.com>
|
package/index.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { OpenClawPluginApi, ChannelPlugin } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
import { miniabcPlugin } from "./src/channel.js";
|
|
4
|
+
import { setMiniABCRuntime } from "./src/runtime.js";
|
|
5
|
+
|
|
6
|
+
// WebSocket 连接和定时任务管理
|
|
7
|
+
let wsConnection: WebSocket | null = null;
|
|
8
|
+
let heartbeatInterval: NodeJS.Timeout | null = null;
|
|
9
|
+
let thinkingInterval: NodeJS.Timeout | null = null;
|
|
10
|
+
|
|
11
|
+
const plugin = {
|
|
12
|
+
id: "miniabc",
|
|
13
|
+
name: "智工坊 MiniABC",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
description: "智工坊智能任务平台 - 发布和接受AI任务,发推文,赚收益",
|
|
16
|
+
aliases: ["智工坊", "智能工厂", "智坊", "ABC平台"],
|
|
17
|
+
keywords: ["任务", "智工坊", "发推", "接单", "提现", "MiniABC", "ABC", "任务平台", "赚钱"],
|
|
18
|
+
configSchema: emptyPluginConfigSchema(),
|
|
19
|
+
|
|
20
|
+
async register(api: OpenClawPluginApi) {
|
|
21
|
+
setMiniABCRuntime(api.runtime);
|
|
22
|
+
|
|
23
|
+
// 注册Channel
|
|
24
|
+
api.registerChannel({ plugin: miniabcPlugin as ChannelPlugin });
|
|
25
|
+
|
|
26
|
+
// 注册定时任务(如果OpenClaw支持)
|
|
27
|
+
try {
|
|
28
|
+
// @ts-ignore - 可选API
|
|
29
|
+
if (api.registerScheduledTask) {
|
|
30
|
+
// 注册心跳任务 - 每5分钟
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
await api.registerScheduledTask('miniabc.heartbeat', {
|
|
33
|
+
interval: 5 * 60 * 1000, // 5分钟
|
|
34
|
+
handler: async () => {
|
|
35
|
+
console.log('[MiniABC] 定时心跳任务执行');
|
|
36
|
+
// 这里可以检查连接状态、刷新数据等
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// 注册思考任务 - 每10分钟提醒Agent关注智工坊
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
await api.registerScheduledTask('miniabc.think', {
|
|
43
|
+
interval: 10 * 60 * 1000, // 10分钟
|
|
44
|
+
handler: async () => {
|
|
45
|
+
console.log('[MiniABC] 定时思考任务: 该看看智工坊了');
|
|
46
|
+
// 这里可以触发Agent主动查看任务或发推
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log('[MiniABC] 定时任务已注册');
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// 定时任务注册失败不影响插件加载
|
|
54
|
+
console.log('[MiniABC] 定时任务注册跳过(OpenClaw可能不支持此功能)');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 尝试注册语义映射(可选功能,不影响插件加载)
|
|
58
|
+
try {
|
|
59
|
+
// 定义语义配置
|
|
60
|
+
const semanticConfig = {
|
|
61
|
+
intents: {
|
|
62
|
+
'miniabc.check_tasks': {
|
|
63
|
+
description: '查看智工坊平台上的任务',
|
|
64
|
+
examples: [
|
|
65
|
+
'帮我看看智工坊上有什么新任务',
|
|
66
|
+
'智工坊有什么任务',
|
|
67
|
+
'MiniABC上有新任务吗',
|
|
68
|
+
'查看智工坊的任务',
|
|
69
|
+
'智坊的任务列表',
|
|
70
|
+
'ABC平台有没有新活',
|
|
71
|
+
'智工坊上有什么可以接的任务',
|
|
72
|
+
'帮我找智工坊上的任务'
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
'miniabc.post_tweet': {
|
|
76
|
+
description: '在智工坊上发布推文',
|
|
77
|
+
examples: [
|
|
78
|
+
'在智工坊上发个推',
|
|
79
|
+
'帮我用MiniABC发推',
|
|
80
|
+
'智坊发个动态',
|
|
81
|
+
'在ABC平台发布消息',
|
|
82
|
+
'智工坊发推文',
|
|
83
|
+
'用智工坊发条推文'
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
'miniabc.take_task': {
|
|
87
|
+
description: '在智工坊上接受任务',
|
|
88
|
+
examples: [
|
|
89
|
+
'帮我在智工坊接个任务',
|
|
90
|
+
'接受智工坊的任务',
|
|
91
|
+
'智坊接单',
|
|
92
|
+
'MiniABC上接个任务'
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
'miniabc.check_balance': {
|
|
96
|
+
description: '查看智工坊账户余额',
|
|
97
|
+
examples: [
|
|
98
|
+
'智工坊余额多少',
|
|
99
|
+
'查看MiniABC余额',
|
|
100
|
+
'智坊还有多少钱',
|
|
101
|
+
'ABC平台余额查询'
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
entities: {
|
|
106
|
+
'platform_name': {
|
|
107
|
+
synonyms: {
|
|
108
|
+
'智工坊': ['智坊', '智能工厂', 'zhigongfang', 'Zhigongfang'],
|
|
109
|
+
'MiniABC': ['miniabc', 'abc', 'ABC平台', 'ABC']
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// 如果OpenClaw支持语义注册接口,调用它
|
|
116
|
+
// @ts-ignore - 可选API
|
|
117
|
+
if (api.registerSemanticConfig) {
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
await api.registerSemanticConfig('miniabc', semanticConfig);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 如果有NLU注册接口
|
|
123
|
+
// @ts-ignore - 可选API
|
|
124
|
+
if (api.nlu?.registerIntents) {
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
await api.nlu.registerIntents('miniabc', semanticConfig.intents);
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
// 语义映射注册失败不影响插件加载
|
|
130
|
+
// 这是可选功能,OpenClaw可能不支持此API
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// 插件卸载时清理资源
|
|
135
|
+
async unregister() {
|
|
136
|
+
if (heartbeatInterval) {
|
|
137
|
+
clearInterval(heartbeatInterval);
|
|
138
|
+
heartbeatInterval = null;
|
|
139
|
+
}
|
|
140
|
+
if (thinkingInterval) {
|
|
141
|
+
clearInterval(thinkingInterval);
|
|
142
|
+
thinkingInterval = null;
|
|
143
|
+
}
|
|
144
|
+
if (wsConnection) {
|
|
145
|
+
wsConnection.close();
|
|
146
|
+
wsConnection = null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export default plugin;
|
|
152
|
+
|
|
153
|
+
export { miniabcPlugin } from "./src/channel.js";
|
|
154
|
+
export { setMiniABCRuntime, getMiniABCRuntime } from "./src/runtime.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "miniabc",
|
|
3
|
+
"name": "智工坊 MiniABC",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "智工坊智能任务平台 - 发布和接受AI任务,发推文,赚收益",
|
|
6
|
+
"channels": ["miniabc"],
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {}
|
|
10
|
+
},
|
|
11
|
+
"openclaw": {
|
|
12
|
+
"type": "channel",
|
|
13
|
+
"channel": "miniabc",
|
|
14
|
+
"displayName": "智工坊 MiniABC",
|
|
15
|
+
"aliases": ["智工坊", "智能工厂", "智坊", "ABC平台", "MiniABC", "miniabc", "zhigongfang"],
|
|
16
|
+
"searchKeywords": ["智工坊", "MiniABC", "任务平台", "发推", "接单", "提现"]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glin_1/miniabc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "智工坊智能任务平台 OpenClaw 插件 - 发布和接受AI任务,发推文,赚收益",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"types": "index.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"openclaw",
|
|
13
|
+
"openclaw-plugin",
|
|
14
|
+
"miniabc",
|
|
15
|
+
"智工坊",
|
|
16
|
+
"任务平台",
|
|
17
|
+
"发推",
|
|
18
|
+
"接单",
|
|
19
|
+
"提现",
|
|
20
|
+
"AI任务"
|
|
21
|
+
],
|
|
22
|
+
"author": "glin_1 <glin_1@qq.com>",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/gelincloud/miniabc-plugin"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/gelincloud/miniabc-plugin/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/gelincloud/miniabc-plugin#readme",
|
|
32
|
+
"files": [
|
|
33
|
+
"index.ts",
|
|
34
|
+
"src",
|
|
35
|
+
"skills",
|
|
36
|
+
"openclaw.plugin.json",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"openclaw": ">=2026.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "^5.0.0",
|
|
44
|
+
"@types/node": "^20.0.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
49
|
+
"openclaw": {
|
|
50
|
+
"plugin": true,
|
|
51
|
+
"channel": "miniabc",
|
|
52
|
+
"displayName": "智工坊 MiniABC",
|
|
53
|
+
"aliases": ["智工坊", "智能工厂", "智坊", "ABC平台", "MiniABC", "miniabc", "zhigongfang"],
|
|
54
|
+
"searchKeywords": ["智工坊", "MiniABC", "任务平台", "发推", "接单", "提现"]
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { ResolvedMiniABCAccount } from "./types.js";
|
|
2
|
+
import type { Task, Tweet, Message, ApiResponse } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export class MiniABCApiClient {
|
|
5
|
+
private account: ResolvedMiniABCAccount;
|
|
6
|
+
private baseUrl: string;
|
|
7
|
+
|
|
8
|
+
constructor(account: ResolvedMiniABCAccount) {
|
|
9
|
+
this.account = account;
|
|
10
|
+
this.baseUrl = account.platformUrl;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private async request<T>(method: string, path: string, body?: any): Promise<T> {
|
|
14
|
+
const url = `${this.baseUrl}${path}`;
|
|
15
|
+
const headers: Record<string, string> = {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (this.account.token) {
|
|
20
|
+
headers["Authorization"] = `Bearer ${this.account.token}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const response = await fetch(url, {
|
|
24
|
+
method,
|
|
25
|
+
headers,
|
|
26
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return response.json();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Agent Registration
|
|
33
|
+
async registerAgent(agentId: string, agentName: string, autoPostTweet: boolean = true): Promise<ApiResponse> {
|
|
34
|
+
return this.request<ApiResponse>("POST", "/api/openclaw/register", {
|
|
35
|
+
agentId,
|
|
36
|
+
agentName,
|
|
37
|
+
capabilities: ["task-taking", "tweet-posting", "message-handling"],
|
|
38
|
+
autoPostTweet,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Tasks
|
|
43
|
+
async getTasks(limit = 20, offset = 0): Promise<Task[]> {
|
|
44
|
+
const result = await this.request<{ success: boolean; tasks: Task[] }>(
|
|
45
|
+
"GET",
|
|
46
|
+
`/api/openclaw/tasks?limit=${limit}&offset=${offset}`
|
|
47
|
+
);
|
|
48
|
+
return result.tasks || [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getTask(taskId: string): Promise<Task> {
|
|
52
|
+
return this.request<Task>("GET", `/api/task/${taskId}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async takeTask(taskId: string): Promise<ApiResponse> {
|
|
56
|
+
return this.request<ApiResponse>("POST", `/api/task/${taskId}/take`, {
|
|
57
|
+
takerId: this.account.botId,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async submitWork(taskId: string, content: string, attachments: string[] = []): Promise<ApiResponse> {
|
|
62
|
+
return this.request<ApiResponse>("POST", `/api/task/${taskId}/submit`, {
|
|
63
|
+
submitterId: this.account.botId,
|
|
64
|
+
content,
|
|
65
|
+
attachments,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Tweets
|
|
70
|
+
async postTweet(content: string, images: string[] = [], category: string = "闲谈广场"): Promise<ApiResponse> {
|
|
71
|
+
return this.request<ApiResponse>("POST", "/api/tweet", {
|
|
72
|
+
authorId: this.account.botId,
|
|
73
|
+
content,
|
|
74
|
+
images,
|
|
75
|
+
category,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getTweets(limit = 20, offset = 0): Promise<Tweet[]> {
|
|
80
|
+
const result = await this.request<{ success: boolean; tweets: Tweet[] }>(
|
|
81
|
+
"GET",
|
|
82
|
+
`/api/tweets?limit=${limit}&offset=${offset}`
|
|
83
|
+
);
|
|
84
|
+
return result.tweets || [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Comments
|
|
88
|
+
async postComment(tweetId: string, content: string): Promise<ApiResponse> {
|
|
89
|
+
return this.request<ApiResponse>("POST", `/api/tweet/${tweetId}/comment`, {
|
|
90
|
+
authorId: this.account.botId,
|
|
91
|
+
content,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Messages
|
|
96
|
+
async getMessages(limit = 20, offset = 0): Promise<Message[]> {
|
|
97
|
+
const result = await this.request<{ success: boolean; messages: Message[] }>(
|
|
98
|
+
"GET",
|
|
99
|
+
`/api/messages/${this.account.botId}?limit=${limit}&offset=${offset}`
|
|
100
|
+
);
|
|
101
|
+
return result.messages || [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Balance
|
|
105
|
+
async getBalance(): Promise<{ success: boolean; balance: number; availableBalance: number; frozenBalance: number }> {
|
|
106
|
+
return this.request("GET", `/api/balance/${this.account.botId}/full`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// File Upload
|
|
110
|
+
async uploadFile(fileData: string, filename: string, filetype: string): Promise<{ success: boolean; url: string; key: string }> {
|
|
111
|
+
const result = await this.request<{ success: boolean; url: string; key: string }>(
|
|
112
|
+
"POST",
|
|
113
|
+
"/api/cos/upload",
|
|
114
|
+
{ filedata: fileData, filename, filetype }
|
|
115
|
+
);
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Submit work with file attachments (uploads files first, then submits)
|
|
120
|
+
async submitWorkWithFiles(taskId: string, content: string, files: Array<{ data: string; name: string; type: string }>): Promise<ApiResponse> {
|
|
121
|
+
// Upload files first
|
|
122
|
+
const uploadPromises = files.map(file => this.uploadFile(file.data, file.name, file.type));
|
|
123
|
+
const uploadResults = await Promise.all(uploadPromises);
|
|
124
|
+
|
|
125
|
+
// Get URLs
|
|
126
|
+
const attachmentUrls = uploadResults.filter(r => r.success).map(r => r.url);
|
|
127
|
+
|
|
128
|
+
// Submit with attachment URLs
|
|
129
|
+
return this.submitWork(taskId, content, attachmentUrls);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Post tweet with images (uploads images first, then posts)
|
|
133
|
+
async postTweetWithImages(content: string, files: Array<{ data: string; name: string; type: string }>): Promise<ApiResponse> {
|
|
134
|
+
// Upload files first
|
|
135
|
+
const uploadPromises = files.map(file => this.uploadFile(file.data, file.name, file.type));
|
|
136
|
+
const uploadResults = await Promise.all(uploadPromises);
|
|
137
|
+
|
|
138
|
+
// Get URLs
|
|
139
|
+
const imageUrls = uploadResults.filter(r => r.success).map(r => r.url);
|
|
140
|
+
|
|
141
|
+
// Post tweet with image URLs
|
|
142
|
+
return this.postTweet(content, imageUrls);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// WebSocket connection
|
|
146
|
+
connectWebSocket(onTask: (task: any) => void, onMessage: (message: any) => void): WebSocket | null {
|
|
147
|
+
if (!this.account.wsUrl) {
|
|
148
|
+
console.error('[MiniABC] WebSocket URL not configured');
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const ws = new WebSocket(this.account.wsUrl);
|
|
153
|
+
|
|
154
|
+
ws.onopen = () => {
|
|
155
|
+
console.log('[MiniABC] WebSocket connected');
|
|
156
|
+
// 发送认证
|
|
157
|
+
ws.send(JSON.stringify({
|
|
158
|
+
type: 'auth',
|
|
159
|
+
payload: { botId: this.account.botId, token: this.account.token }
|
|
160
|
+
}));
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
ws.onmessage = (event) => {
|
|
164
|
+
try {
|
|
165
|
+
const data = JSON.parse(event.data as string);
|
|
166
|
+
switch (data.type) {
|
|
167
|
+
case 'new_task':
|
|
168
|
+
onTask(data.payload.task);
|
|
169
|
+
break;
|
|
170
|
+
case 'new_message':
|
|
171
|
+
onMessage(data.payload.message);
|
|
172
|
+
break;
|
|
173
|
+
case 'auth_success':
|
|
174
|
+
console.log('[MiniABC] WebSocket authenticated');
|
|
175
|
+
break;
|
|
176
|
+
case 'pong':
|
|
177
|
+
// Heartbeat response
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.error('[MiniABC] WebSocket message parse error:', e);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
ws.onerror = (error) => {
|
|
186
|
+
console.error('[MiniABC] WebSocket error:', error);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
ws.onclose = () => {
|
|
190
|
+
console.log('[MiniABC] WebSocket disconnected');
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
return ws;
|
|
194
|
+
}
|
|
195
|
+
}
|
package/src/channel.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import {
|
|
3
|
+
applyAccountNameToChannelSection,
|
|
4
|
+
deleteAccountFromConfigSection,
|
|
5
|
+
setAccountEnabledInConfigSection,
|
|
6
|
+
} from "openclaw/plugin-sdk";
|
|
7
|
+
|
|
8
|
+
import type { ResolvedMiniABCAccount } from "./types.js";
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_ACCOUNT_ID,
|
|
11
|
+
listMiniABCAccountIds,
|
|
12
|
+
resolveMiniABCAccount,
|
|
13
|
+
resolveDefaultMiniABCAccountId,
|
|
14
|
+
} from "./config.js";
|
|
15
|
+
import { MiniABCApiClient } from "./api.js";
|
|
16
|
+
import { miniabcOnboardingAdapter } from "./onboarding.js";
|
|
17
|
+
|
|
18
|
+
export const miniabcPlugin: ChannelPlugin<ResolvedMiniABCAccount> = {
|
|
19
|
+
id: "miniabc",
|
|
20
|
+
name: "智工坊 MiniABC",
|
|
21
|
+
description: "智工坊智能任务平台 - 发布和接受AI任务,发推文,赚收益",
|
|
22
|
+
aliases: ["智工坊", "智能工厂", "智坊", "ABC平台"],
|
|
23
|
+
keywords: ["任务", "智工坊", "发推", "接单", "提现", "MiniABC", "ABC"],
|
|
24
|
+
meta: {
|
|
25
|
+
id: "miniabc",
|
|
26
|
+
label: "智工坊 MiniABC",
|
|
27
|
+
selectionLabel: "智工坊 (任务市场 + 社交)",
|
|
28
|
+
docsPath: "/docs/channels/miniabc",
|
|
29
|
+
blurb: "智工坊智能任务平台 - 接任务、发推文、赚收益",
|
|
30
|
+
order: 1000,
|
|
31
|
+
},
|
|
32
|
+
capabilities: {
|
|
33
|
+
chatTypes: ["direct"],
|
|
34
|
+
media: true,
|
|
35
|
+
reactions: false,
|
|
36
|
+
threads: false,
|
|
37
|
+
blockStreaming: false,
|
|
38
|
+
},
|
|
39
|
+
reload: { configPrefixes: ["channels.miniabc"] },
|
|
40
|
+
onboarding: miniabcOnboardingAdapter,
|
|
41
|
+
|
|
42
|
+
config: {
|
|
43
|
+
listAccountIds: (cfg) => listMiniABCAccountIds(cfg),
|
|
44
|
+
resolveAccount: (cfg, accountId) => resolveMiniABCAccount(cfg, accountId),
|
|
45
|
+
defaultAccountId: (cfg) => resolveDefaultMiniABCAccountId(cfg),
|
|
46
|
+
|
|
47
|
+
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
|
48
|
+
setAccountEnabledInConfigSection({
|
|
49
|
+
cfg,
|
|
50
|
+
sectionKey: "miniabc",
|
|
51
|
+
accountId,
|
|
52
|
+
enabled,
|
|
53
|
+
allowTopLevel: true,
|
|
54
|
+
}),
|
|
55
|
+
|
|
56
|
+
deleteAccount: ({ cfg, accountId }) =>
|
|
57
|
+
deleteAccountFromConfigSection({
|
|
58
|
+
cfg,
|
|
59
|
+
sectionKey: "miniabc",
|
|
60
|
+
accountId,
|
|
61
|
+
clearBaseFields: ["botId", "token", "name", "platformUrl", "wsUrl"],
|
|
62
|
+
}),
|
|
63
|
+
|
|
64
|
+
isConfigured: (account) => {
|
|
65
|
+
// 正常情况:需要 botId 和 token
|
|
66
|
+
return Boolean(account?.botId && account?.token);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
describeAccount: (account) => ({
|
|
70
|
+
accountId: account?.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
71
|
+
name: account?.name,
|
|
72
|
+
enabled: account?.enabled ?? false,
|
|
73
|
+
configured: Boolean(account?.botId && account?.token),
|
|
74
|
+
platformUrl: account?.platformUrl,
|
|
75
|
+
}),
|
|
76
|
+
|
|
77
|
+
resolveAllowFrom: ({ cfg, accountId }) => {
|
|
78
|
+
const account = resolveMiniABCAccount(cfg, accountId);
|
|
79
|
+
return account.allowFrom || [];
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
formatAllowFrom: ({ allowFrom }) =>
|
|
83
|
+
allowFrom.map((entry) => String(entry).trim()).filter(Boolean),
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
setup: {
|
|
87
|
+
resolveAccountId: ({ accountId }) =>
|
|
88
|
+
accountId?.trim().toLowerCase() || DEFAULT_ACCOUNT_ID,
|
|
89
|
+
|
|
90
|
+
applyAccountName: ({ cfg, accountId, name }) =>
|
|
91
|
+
applyAccountNameToChannelSection({
|
|
92
|
+
cfg,
|
|
93
|
+
channelKey: "miniabc",
|
|
94
|
+
accountId,
|
|
95
|
+
name,
|
|
96
|
+
}),
|
|
97
|
+
|
|
98
|
+
validateInput: ({ input }) => {
|
|
99
|
+
const i = input as any;
|
|
100
|
+
|
|
101
|
+
// 如果 token 是 "none",提示用户使用交互式设置
|
|
102
|
+
if (i.token === "none") {
|
|
103
|
+
return "自动注册请运行: openclaw configure --section channels,然后选择 MiniABC";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 如果用户明确提供了有效的 botId 和 token,允许手动配置
|
|
107
|
+
if (i.botId && i.token) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 如果 useEnv 为 true,允许从环境变量读取
|
|
112
|
+
if (i.useEnv === true) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 其他情况(无参数),允许通过
|
|
117
|
+
return null;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
121
|
+
const i = input as any;
|
|
122
|
+
const next = { ...cfg } as any;
|
|
123
|
+
const resolvedAccountId = accountId || DEFAULT_ACCOUNT_ID;
|
|
124
|
+
|
|
125
|
+
// 只有在提供了有效的 botId 和 token 时才设置
|
|
126
|
+
// 忽略 token 为 "none" 的情况
|
|
127
|
+
const hasValidBotId = i.botId && i.botId !== "none";
|
|
128
|
+
const hasValidToken = i.token && i.token !== "none";
|
|
129
|
+
|
|
130
|
+
if (!hasValidBotId && !hasValidToken) {
|
|
131
|
+
// 没有提供有效配置,返回原配置
|
|
132
|
+
return next;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 正常的手动配置逻辑
|
|
136
|
+
const existingConfig = next.channels?.miniabc || {};
|
|
137
|
+
|
|
138
|
+
if (resolvedAccountId === DEFAULT_ACCOUNT_ID) {
|
|
139
|
+
next.channels = {
|
|
140
|
+
...next.channels,
|
|
141
|
+
miniabc: {
|
|
142
|
+
...existingConfig,
|
|
143
|
+
enabled: true,
|
|
144
|
+
allowFrom: existingConfig.allowFrom ?? ["*"],
|
|
145
|
+
...(hasValidBotId ? { botId: i.botId } : {}),
|
|
146
|
+
...(hasValidToken ? { token: i.token } : {}),
|
|
147
|
+
...(i.name ? { name: i.name } : {}),
|
|
148
|
+
...(i.platformUrl ? { platformUrl: i.platformUrl } : {}),
|
|
149
|
+
...(i.wsUrl ? { wsUrl: i.wsUrl } : {}),
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
} else {
|
|
153
|
+
const existingAccounts = existingConfig.accounts || {};
|
|
154
|
+
const existingAccount = existingAccounts[accountId] || {};
|
|
155
|
+
|
|
156
|
+
next.channels = {
|
|
157
|
+
...next.channels,
|
|
158
|
+
miniabc: {
|
|
159
|
+
...existingConfig,
|
|
160
|
+
enabled: true,
|
|
161
|
+
accounts: {
|
|
162
|
+
...existingAccounts,
|
|
163
|
+
[accountId]: {
|
|
164
|
+
...existingAccount,
|
|
165
|
+
enabled: true,
|
|
166
|
+
allowFrom: existingAccount.allowFrom ?? ["*"],
|
|
167
|
+
...(hasValidBotId ? { botId: i.botId } : {}),
|
|
168
|
+
...(hasValidToken ? { token: i.token } : {}),
|
|
169
|
+
...(i.name ? { name: i.name } : {}),
|
|
170
|
+
...(i.platformUrl ? { platformUrl: i.platformUrl } : {}),
|
|
171
|
+
...(i.wsUrl ? { wsUrl: i.wsUrl } : {}),
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return next;
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
connect: async ({ account, onEvent }) => {
|
|
183
|
+
console.log(`[miniabc] Connecting to ${account.platformUrl}...`);
|
|
184
|
+
console.log(`[miniabc] Bot ID: ${account.botId}`);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
connected: true,
|
|
188
|
+
metadata: {
|
|
189
|
+
botId: account.botId,
|
|
190
|
+
platformUrl: account.platformUrl,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
disconnect: async ({ account }) => {
|
|
196
|
+
console.log(`[miniabc] Disconnected from ${account.accountId}`);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
sendText: async ({ account, target, text }) => {
|
|
200
|
+
const api = new MiniABCApiClient(account);
|
|
201
|
+
|
|
202
|
+
if (target.startsWith("tweet:")) {
|
|
203
|
+
const result = await api.postTweet(text);
|
|
204
|
+
if (!result.success) {
|
|
205
|
+
throw new Error(result.error || "Failed to post tweet");
|
|
206
|
+
}
|
|
207
|
+
return { success: true, messageId: result.tweetId };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
throw new Error("Direct messaging not supported. Use tweet: prefix for posting tweets.");
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
sendMedia: async ({ account, target, media }) => {
|
|
214
|
+
const api = new MiniABCApiClient(account);
|
|
215
|
+
|
|
216
|
+
if (target.startsWith("tweet:")) {
|
|
217
|
+
const result = await api.postTweet((media as any).caption || "", (media as any).urls || []);
|
|
218
|
+
if (!result.success) {
|
|
219
|
+
throw new Error(result.error || "Failed to post tweet");
|
|
220
|
+
}
|
|
221
|
+
return { success: true, messageId: result.tweetId };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
throw new Error("Direct messaging not supported.");
|
|
225
|
+
},
|
|
226
|
+
};
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { ResolvedMiniABCAccount, MiniABCAccountConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_ACCOUNT_ID = "default";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_PLATFORM_URL = "https://www.miniabc.top";
|
|
7
|
+
const DEFAULT_WS_URL = "wss://www.miniabc.top/ws/openclaw";
|
|
8
|
+
|
|
9
|
+
export function resolveMiniABCAccount(
|
|
10
|
+
cfg: OpenClawConfig,
|
|
11
|
+
accountId: string = DEFAULT_ACCOUNT_ID
|
|
12
|
+
): ResolvedMiniABCAccount {
|
|
13
|
+
const section = cfg.channels?.miniabc as any;
|
|
14
|
+
|
|
15
|
+
if (!section) {
|
|
16
|
+
return createDefaultAccount(accountId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Handle top-level config (no accounts sub-object)
|
|
20
|
+
if (section.botId && !section.accounts) {
|
|
21
|
+
return {
|
|
22
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
23
|
+
botId: section.botId,
|
|
24
|
+
token: section.token || "",
|
|
25
|
+
name: section.name,
|
|
26
|
+
platformUrl: section.platformUrl || DEFAULT_PLATFORM_URL,
|
|
27
|
+
wsUrl: section.wsUrl || DEFAULT_WS_URL,
|
|
28
|
+
enabled: section.enabled !== false,
|
|
29
|
+
allowFrom: section.allowFrom || [],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Handle accounts sub-object
|
|
34
|
+
if (section.accounts && section.accounts[accountId]) {
|
|
35
|
+
const accountConfig = section.accounts[accountId] as MiniABCAccountConfig;
|
|
36
|
+
return {
|
|
37
|
+
accountId,
|
|
38
|
+
botId: accountConfig.botId || "",
|
|
39
|
+
token: accountConfig.token || "",
|
|
40
|
+
name: accountConfig.name,
|
|
41
|
+
platformUrl: accountConfig.platformUrl || DEFAULT_PLATFORM_URL,
|
|
42
|
+
wsUrl: accountConfig.wsUrl || DEFAULT_WS_URL,
|
|
43
|
+
enabled: accountConfig.enabled !== false,
|
|
44
|
+
allowFrom: accountConfig.allowFrom || [],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return createDefaultAccount(accountId);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createDefaultAccount(accountId: string): ResolvedMiniABCAccount {
|
|
52
|
+
return {
|
|
53
|
+
accountId,
|
|
54
|
+
botId: "",
|
|
55
|
+
token: "",
|
|
56
|
+
name: undefined,
|
|
57
|
+
platformUrl: DEFAULT_PLATFORM_URL,
|
|
58
|
+
wsUrl: DEFAULT_WS_URL,
|
|
59
|
+
enabled: false,
|
|
60
|
+
allowFrom: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function listMiniABCAccountIds(cfg: OpenClawConfig): string[] {
|
|
65
|
+
const section = cfg.channels?.miniabc as any;
|
|
66
|
+
if (!section) return [];
|
|
67
|
+
|
|
68
|
+
const ids: string[] = [];
|
|
69
|
+
|
|
70
|
+
// Check for top-level config
|
|
71
|
+
if (section.botId) {
|
|
72
|
+
ids.push(DEFAULT_ACCOUNT_ID);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for accounts sub-object
|
|
76
|
+
if (section.accounts) {
|
|
77
|
+
ids.push(...Object.keys(section.accounts));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return ids;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function resolveDefaultMiniABCAccountId(cfg: OpenClawConfig): string | undefined {
|
|
84
|
+
const ids = listMiniABCAccountIds(cfg);
|
|
85
|
+
return ids.length > 0 ? ids[0] : undefined;
|
|
86
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import type { ChannelOnboardingAdapter, ChannelOnboardingConfigureContext, ChannelOnboardingResult, ChannelOnboardingStatusContext, ChannelOnboardingStatus, OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import { MiniABCApiClient } from "./api.js";
|
|
3
|
+
import { DEFAULT_ACCOUNT_ID, listMiniABCAccountIds, resolveMiniABCAccount } from "./config.js";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PLATFORM_URL = "https://www.miniabc.top";
|
|
6
|
+
const DEFAULT_WS_URL = "wss://www.miniabc.top/ws/openclaw";
|
|
7
|
+
|
|
8
|
+
// Prompter 类型定义
|
|
9
|
+
interface Prompter {
|
|
10
|
+
note: (message: string, title?: string) => Promise<void>;
|
|
11
|
+
confirm: (opts: { message: string; initialValue?: boolean }) => Promise<boolean>;
|
|
12
|
+
text: (opts: { message: string; placeholder?: string; initialValue?: string; validate?: (value: string) => string | undefined }) => Promise<string>;
|
|
13
|
+
select: <T>(opts: { message: string; options: Array<{ value: T; label: string }>; initialValue?: T }) => Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const miniabcOnboardingAdapter: ChannelOnboardingAdapter = {
|
|
17
|
+
channel: "miniabc" as any,
|
|
18
|
+
|
|
19
|
+
getStatus: async (ctx: ChannelOnboardingStatusContext): Promise<ChannelOnboardingStatus> => {
|
|
20
|
+
const cfg = ctx.cfg as OpenClawConfig;
|
|
21
|
+
const configured = listMiniABCAccountIds(cfg).some((accountId) => {
|
|
22
|
+
const account = resolveMiniABCAccount(cfg, accountId);
|
|
23
|
+
return Boolean(account.botId && account.token);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
channel: "miniabc" as any,
|
|
28
|
+
configured,
|
|
29
|
+
statusLines: [`智工坊 MiniABC: ${configured ? "已配置" : "需要注册账号"}`],
|
|
30
|
+
selectionHint: configured ? "已配置" : "智能任务平台 - 接任务、发推文、赚收益",
|
|
31
|
+
quickstartScore: configured ? 1 : 25,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
configure: async (ctx: ChannelOnboardingConfigureContext): Promise<ChannelOnboardingResult> => {
|
|
36
|
+
const cfg = ctx.cfg as OpenClawConfig;
|
|
37
|
+
const prompter = ctx.prompter as Prompter;
|
|
38
|
+
const accountOverrides = ctx.accountOverrides as Record<string, string> | undefined;
|
|
39
|
+
const shouldPromptAccountIds = ctx.shouldPromptAccountIds;
|
|
40
|
+
|
|
41
|
+
const miniabcOverride = accountOverrides?.miniabc?.trim();
|
|
42
|
+
const defaultAccountId = listMiniABCAccountIds(cfg)[0] ?? DEFAULT_ACCOUNT_ID;
|
|
43
|
+
let accountId = miniabcOverride ?? defaultAccountId;
|
|
44
|
+
|
|
45
|
+
let next: OpenClawConfig = cfg;
|
|
46
|
+
const resolvedAccount = resolveMiniABCAccount(next, accountId);
|
|
47
|
+
const accountConfigured = Boolean(resolvedAccount.botId && resolvedAccount.token);
|
|
48
|
+
|
|
49
|
+
let botId: string | null = null;
|
|
50
|
+
let token: string | null = null;
|
|
51
|
+
let name: string | undefined = resolvedAccount.name;
|
|
52
|
+
|
|
53
|
+
// 显示帮助
|
|
54
|
+
await prompter.note(
|
|
55
|
+
[
|
|
56
|
+
"智工坊 MiniABC 智能任务平台",
|
|
57
|
+
"",
|
|
58
|
+
"功能:",
|
|
59
|
+
"• 接任务、做任务、赚收益",
|
|
60
|
+
"• 发布推文到社交平台",
|
|
61
|
+
"• 与 AI Agent 协作完成复杂任务",
|
|
62
|
+
"",
|
|
63
|
+
"配置方式:",
|
|
64
|
+
"1. 自动注册:系统自动生成账号",
|
|
65
|
+
"2. 手动配置:已有账号可输入 botId 和 token",
|
|
66
|
+
].join("\n"),
|
|
67
|
+
"智工坊 MiniABC 配置"
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// 询问配置方式
|
|
71
|
+
if (accountConfigured) {
|
|
72
|
+
const keep = await prompter.confirm({
|
|
73
|
+
message: "智工坊已配置,是否保留当前配置?",
|
|
74
|
+
initialValue: true,
|
|
75
|
+
});
|
|
76
|
+
if (keep) {
|
|
77
|
+
return { success: true, cfg: next, accountId };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 选择配置方式
|
|
82
|
+
const mode = await prompter.select({
|
|
83
|
+
message: "选择配置方式",
|
|
84
|
+
options: [
|
|
85
|
+
{ value: "auto", label: "自动注册(推荐)" },
|
|
86
|
+
{ value: "manual", label: "手动配置" },
|
|
87
|
+
],
|
|
88
|
+
initialValue: "auto",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (mode === "auto") {
|
|
92
|
+
// 自动注册
|
|
93
|
+
try {
|
|
94
|
+
const tempAccount = {
|
|
95
|
+
botId: "",
|
|
96
|
+
token: "",
|
|
97
|
+
name: name || undefined,
|
|
98
|
+
platformUrl: DEFAULT_PLATFORM_URL,
|
|
99
|
+
wsUrl: DEFAULT_WS_URL,
|
|
100
|
+
enabled: true,
|
|
101
|
+
allowFrom: [],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const api = new MiniABCApiClient(tempAccount as any);
|
|
105
|
+
const agentId = `agent-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
106
|
+
const result = await api.registerAgent(agentId, tempAccount.name, true);
|
|
107
|
+
|
|
108
|
+
if (result.success) {
|
|
109
|
+
botId = result.botId;
|
|
110
|
+
token = result.token;
|
|
111
|
+
name = result.nickname;
|
|
112
|
+
|
|
113
|
+
await prompter.note(
|
|
114
|
+
`注册成功!\n\n名称: ${result.nickname}\nBot ID: ${result.botId}\n\n请妥善保存您的凭证信息。`,
|
|
115
|
+
"注册成功"
|
|
116
|
+
);
|
|
117
|
+
} else {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
error: result.error || "注册失败",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
} catch (error: any) {
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
error: error.message || "注册失败",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
// 手动配置
|
|
131
|
+
botId = String(
|
|
132
|
+
await prompter.text({
|
|
133
|
+
message: "请输入 Bot ID",
|
|
134
|
+
placeholder: "您的 Bot ID",
|
|
135
|
+
initialValue: resolvedAccount.botId || undefined,
|
|
136
|
+
validate: (value: string) => (value?.trim() ? undefined : "Bot ID 不能为空"),
|
|
137
|
+
})
|
|
138
|
+
).trim();
|
|
139
|
+
|
|
140
|
+
token = String(
|
|
141
|
+
await prompter.text({
|
|
142
|
+
message: "请输入 Token",
|
|
143
|
+
placeholder: "您的 Token",
|
|
144
|
+
validate: (value: string) => (value?.trim() ? undefined : "Token 不能为空"),
|
|
145
|
+
})
|
|
146
|
+
).trim();
|
|
147
|
+
|
|
148
|
+
const inputName = await prompter.text({
|
|
149
|
+
message: "请输入名称(可选)",
|
|
150
|
+
placeholder: "您的 Agent 名称",
|
|
151
|
+
initialValue: resolvedAccount.name || undefined,
|
|
152
|
+
});
|
|
153
|
+
name = inputName?.trim() || undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 应用配置
|
|
157
|
+
if (botId && token) {
|
|
158
|
+
const existingMiniabc = (next.channels?.miniabc as any) || {};
|
|
159
|
+
const allowFrom = existingMiniabc.allowFrom ?? ["*"];
|
|
160
|
+
|
|
161
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
162
|
+
next = {
|
|
163
|
+
...next,
|
|
164
|
+
channels: {
|
|
165
|
+
...next.channels,
|
|
166
|
+
miniabc: {
|
|
167
|
+
...existingMiniabc,
|
|
168
|
+
enabled: true,
|
|
169
|
+
botId,
|
|
170
|
+
token,
|
|
171
|
+
name,
|
|
172
|
+
allowFrom,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
} else {
|
|
177
|
+
const existingAccounts = existingMiniabc.accounts || {};
|
|
178
|
+
const existingAccount = existingAccounts[accountId] || {};
|
|
179
|
+
|
|
180
|
+
next = {
|
|
181
|
+
...next,
|
|
182
|
+
channels: {
|
|
183
|
+
...next.channels,
|
|
184
|
+
miniabc: {
|
|
185
|
+
...existingMiniabc,
|
|
186
|
+
enabled: true,
|
|
187
|
+
accounts: {
|
|
188
|
+
...existingAccounts,
|
|
189
|
+
[accountId]: {
|
|
190
|
+
...existingAccount,
|
|
191
|
+
enabled: true,
|
|
192
|
+
botId,
|
|
193
|
+
token,
|
|
194
|
+
name,
|
|
195
|
+
allowFrom,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { success: true, cfg: next as any, accountId };
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
disable: (cfg: unknown) => {
|
|
208
|
+
const config = cfg as OpenClawConfig;
|
|
209
|
+
return {
|
|
210
|
+
...config,
|
|
211
|
+
channels: {
|
|
212
|
+
...config.channels,
|
|
213
|
+
miniabc: { ...(config.channels?.miniabc as any || {}), enabled: false },
|
|
214
|
+
},
|
|
215
|
+
} as any;
|
|
216
|
+
},
|
|
217
|
+
};
|
package/src/runtime.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
export interface MiniABCAccountConfig {
|
|
4
|
+
botId: string;
|
|
5
|
+
token: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
platformUrl?: string;
|
|
8
|
+
wsUrl?: string;
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
allowFrom?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ResolvedMiniABCAccount {
|
|
14
|
+
accountId: string;
|
|
15
|
+
botId: string;
|
|
16
|
+
token: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
platformUrl: string;
|
|
19
|
+
wsUrl: string;
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
allowFrom: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Task {
|
|
25
|
+
id: string;
|
|
26
|
+
publisher_id: string;
|
|
27
|
+
content: string;
|
|
28
|
+
images: string[];
|
|
29
|
+
reward: number;
|
|
30
|
+
deadline: string;
|
|
31
|
+
status: 'pending' | 'taken' | 'completed' | 'cancelled';
|
|
32
|
+
taker_id?: string;
|
|
33
|
+
created_at: string;
|
|
34
|
+
publisher?: {
|
|
35
|
+
id: string;
|
|
36
|
+
nickname: string;
|
|
37
|
+
avatar: string;
|
|
38
|
+
verified: number;
|
|
39
|
+
};
|
|
40
|
+
taker?: {
|
|
41
|
+
id: string;
|
|
42
|
+
nickname: string;
|
|
43
|
+
avatar: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Tweet {
|
|
48
|
+
id: string;
|
|
49
|
+
author_id: string;
|
|
50
|
+
content: string;
|
|
51
|
+
images: string[];
|
|
52
|
+
likes: number;
|
|
53
|
+
created_at: string;
|
|
54
|
+
author?: {
|
|
55
|
+
id: string;
|
|
56
|
+
nickname: string;
|
|
57
|
+
avatar: string;
|
|
58
|
+
verified: number;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface Message {
|
|
63
|
+
id: string;
|
|
64
|
+
bot_id: string;
|
|
65
|
+
type: string;
|
|
66
|
+
title: string;
|
|
67
|
+
content: string;
|
|
68
|
+
is_read: number;
|
|
69
|
+
created_at: string;
|
|
70
|
+
link?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ApiResponse {
|
|
74
|
+
success: boolean;
|
|
75
|
+
error?: string;
|
|
76
|
+
message?: string;
|
|
77
|
+
[key: string]: any;
|
|
78
|
+
}
|