@onebots/adapter-wecom 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/LICENSE +21 -0
- package/README.md +64 -0
- package/lib/adapter.d.ts +93 -0
- package/lib/adapter.js +347 -0
- package/lib/bot.d.ts +71 -0
- package/lib/bot.js +191 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +3 -0
- package/lib/types.d.ts +155 -0
- package/lib/types.js +6 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 凉菜
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @onebots/adapter-wecom
|
|
2
|
+
|
|
3
|
+
onebots 企业微信适配器
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @onebots/adapter-wecom
|
|
9
|
+
# 或
|
|
10
|
+
pnpm add @onebots/adapter-wecom
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 配置
|
|
14
|
+
|
|
15
|
+
在 `config.yaml` 中配置:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
wecom.your_bot_id:
|
|
19
|
+
corp_id: "YOUR_CORP_ID"
|
|
20
|
+
corp_secret: "YOUR_CORP_SECRET"
|
|
21
|
+
agent_id: "YOUR_AGENT_ID"
|
|
22
|
+
token: "YOUR_TOKEN" # 可选,回调验证 Token
|
|
23
|
+
encoding_aes_key: "YOUR_ENCODING_AES_KEY" # 可选,消息加解密密钥
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 使用
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
onebots -r wecom
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 功能
|
|
33
|
+
|
|
34
|
+
- ✅ 应用消息推送
|
|
35
|
+
- 文本消息
|
|
36
|
+
- 图片消息
|
|
37
|
+
- 视频消息
|
|
38
|
+
- 文件消息
|
|
39
|
+
- 文本卡片
|
|
40
|
+
- Markdown 消息
|
|
41
|
+
- ✅ 通讯录管理
|
|
42
|
+
- 获取用户信息
|
|
43
|
+
- 获取部门列表
|
|
44
|
+
- 获取部门成员列表
|
|
45
|
+
- ✅ 事件订阅
|
|
46
|
+
- 通讯录变更事件
|
|
47
|
+
- 应用消息回调
|
|
48
|
+
|
|
49
|
+
## 获取应用凭证
|
|
50
|
+
|
|
51
|
+
1. 访问 [企业微信管理后台](https://work.weixin.qq.com/)
|
|
52
|
+
2. 应用管理 → 创建应用
|
|
53
|
+
3. 获取 `企业ID`(CorpID)
|
|
54
|
+
4. 获取 `应用Secret`(CorpSecret)
|
|
55
|
+
5. 获取 `应用ID`(AgentID)
|
|
56
|
+
6. 配置回调 URL:`http://your-server:port/wecom/{account_id}/webhook`
|
|
57
|
+
7. 获取 `Token` 和 `EncodingAESKey`(如果启用加密)
|
|
58
|
+
|
|
59
|
+
## 相关链接
|
|
60
|
+
|
|
61
|
+
- [企业微信开放平台](https://developer.work.weixin.qq.com/)
|
|
62
|
+
- [企业微信应用开发文档](https://developer.work.weixin.qq.com/document/path/90488)
|
|
63
|
+
- [onebots 文档](https://onebots.js.org/)
|
|
64
|
+
|
package/lib/adapter.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信适配器
|
|
3
|
+
* 继承 Adapter 基类,实现企业微信平台功能
|
|
4
|
+
*/
|
|
5
|
+
import { Account } from "onebots";
|
|
6
|
+
import { Adapter } from "onebots";
|
|
7
|
+
import { BaseApp } from "onebots";
|
|
8
|
+
import { WeComBot } from "./bot.js";
|
|
9
|
+
import type { WeComConfig } from "./types.js";
|
|
10
|
+
export declare class WeComAdapter extends Adapter<WeComBot, "wecom"> {
|
|
11
|
+
constructor(app: BaseApp);
|
|
12
|
+
/**
|
|
13
|
+
* 发送消息
|
|
14
|
+
*/
|
|
15
|
+
sendMessage(uin: string, params: Adapter.SendMessageParams): Promise<Adapter.SendMessageResult>;
|
|
16
|
+
/**
|
|
17
|
+
* 删除/撤回消息
|
|
18
|
+
*/
|
|
19
|
+
deleteMessage(uin: string, params: Adapter.DeleteMessageParams): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* 获取消息
|
|
22
|
+
*/
|
|
23
|
+
getMessage(uin: string, params: Adapter.GetMessageParams): Promise<Adapter.MessageInfo>;
|
|
24
|
+
/**
|
|
25
|
+
* 更新消息
|
|
26
|
+
*/
|
|
27
|
+
updateMessage(uin: string, params: Adapter.UpdateMessageParams): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* 获取机器人自身信息
|
|
30
|
+
*/
|
|
31
|
+
getLoginInfo(uin: string): Promise<Adapter.UserInfo>;
|
|
32
|
+
/**
|
|
33
|
+
* 获取用户信息
|
|
34
|
+
*/
|
|
35
|
+
getUserInfo(uin: string, params: Adapter.GetUserInfoParams): Promise<Adapter.UserInfo>;
|
|
36
|
+
/**
|
|
37
|
+
* 获取好友列表(企业微信不支持)
|
|
38
|
+
*/
|
|
39
|
+
getFriendList(uin: string, params?: Adapter.GetFriendListParams): Promise<Adapter.FriendInfo[]>;
|
|
40
|
+
/**
|
|
41
|
+
* 获取好友信息
|
|
42
|
+
*/
|
|
43
|
+
getFriendInfo(uin: string, params: Adapter.GetFriendInfoParams): Promise<Adapter.FriendInfo>;
|
|
44
|
+
/**
|
|
45
|
+
* 获取群列表(部门列表)
|
|
46
|
+
*/
|
|
47
|
+
getGroupList(uin: string, params?: Adapter.GetGroupListParams): Promise<Adapter.GroupInfo[]>;
|
|
48
|
+
/**
|
|
49
|
+
* 获取群信息(部门信息)
|
|
50
|
+
*/
|
|
51
|
+
getGroupInfo(uin: string, params: Adapter.GetGroupInfoParams): Promise<Adapter.GroupInfo>;
|
|
52
|
+
/**
|
|
53
|
+
* 退出群组(企业微信不支持)
|
|
54
|
+
*/
|
|
55
|
+
leaveGroup(uin: string, params: Adapter.LeaveGroupParams): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* 获取群成员列表(部门成员列表)
|
|
58
|
+
*/
|
|
59
|
+
getGroupMemberList(uin: string, params: Adapter.GetGroupMemberListParams): Promise<Adapter.GroupMemberInfo[]>;
|
|
60
|
+
/**
|
|
61
|
+
* 获取群成员信息
|
|
62
|
+
*/
|
|
63
|
+
getGroupMemberInfo(uin: string, params: Adapter.GetGroupMemberInfoParams): Promise<Adapter.GroupMemberInfo>;
|
|
64
|
+
/**
|
|
65
|
+
* 踢出群成员(企业微信不支持)
|
|
66
|
+
*/
|
|
67
|
+
kickGroupMember(uin: string, params: Adapter.KickGroupMemberParams): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* 设置群名片(企业微信不支持)
|
|
70
|
+
*/
|
|
71
|
+
setGroupCard(uin: string, params: Adapter.SetGroupCardParams): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* 获取版本信息
|
|
74
|
+
*/
|
|
75
|
+
getVersion(uin: string): Promise<Adapter.VersionInfo>;
|
|
76
|
+
/**
|
|
77
|
+
* 获取运行状态
|
|
78
|
+
*/
|
|
79
|
+
getStatus(uin: string): Promise<Adapter.StatusInfo>;
|
|
80
|
+
createAccount(config: Account.Config<'wecom'>): Account<'wecom', WeComBot>;
|
|
81
|
+
/**
|
|
82
|
+
* 处理企业微信事件
|
|
83
|
+
*/
|
|
84
|
+
private handleWeComEvent;
|
|
85
|
+
}
|
|
86
|
+
declare module "onebots" {
|
|
87
|
+
namespace Adapter {
|
|
88
|
+
interface Configs {
|
|
89
|
+
wecom: WeComConfig;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=adapter.d.ts.map
|
package/lib/adapter.js
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信适配器
|
|
3
|
+
* 继承 Adapter 基类,实现企业微信平台功能
|
|
4
|
+
*/
|
|
5
|
+
import { Account, AdapterRegistry, AccountStatus } from "onebots";
|
|
6
|
+
import { Adapter } from "onebots";
|
|
7
|
+
import { WeComBot } from "./bot.js";
|
|
8
|
+
export class WeComAdapter extends Adapter {
|
|
9
|
+
constructor(app) {
|
|
10
|
+
super(app, "wecom");
|
|
11
|
+
this.icon = "https://work.weixin.qq.com/favicon.ico";
|
|
12
|
+
}
|
|
13
|
+
// ============================================
|
|
14
|
+
// 消息相关方法
|
|
15
|
+
// ============================================
|
|
16
|
+
/**
|
|
17
|
+
* 发送消息
|
|
18
|
+
*/
|
|
19
|
+
async sendMessage(uin, params) {
|
|
20
|
+
const account = this.getAccount(uin);
|
|
21
|
+
if (!account)
|
|
22
|
+
throw new Error(`Account ${uin} not found`);
|
|
23
|
+
const bot = account.client;
|
|
24
|
+
const { scene_id, scene_type, message } = params;
|
|
25
|
+
// 解析消息内容
|
|
26
|
+
let text = '';
|
|
27
|
+
const request = {
|
|
28
|
+
msgtype: 'text',
|
|
29
|
+
agentid: parseInt(bot.getCachedMe()?.userid || bot['config'].agent_id),
|
|
30
|
+
};
|
|
31
|
+
// 根据场景类型设置接收者
|
|
32
|
+
if (scene_type === 'private' || scene_type === 'direct') {
|
|
33
|
+
request.touser = scene_id.string;
|
|
34
|
+
}
|
|
35
|
+
else if (scene_type === 'group') {
|
|
36
|
+
// 企业微信群聊通过 toparty 或 totag
|
|
37
|
+
request.toparty = scene_id.string;
|
|
38
|
+
}
|
|
39
|
+
for (const seg of message) {
|
|
40
|
+
if (typeof seg === 'string') {
|
|
41
|
+
text += seg;
|
|
42
|
+
}
|
|
43
|
+
else if (seg.type === 'text') {
|
|
44
|
+
text += seg.data.text || '';
|
|
45
|
+
}
|
|
46
|
+
else if (seg.type === 'at') {
|
|
47
|
+
const userId = seg.data.qq || seg.data.id || seg.data.user_id;
|
|
48
|
+
if (userId === 'all') {
|
|
49
|
+
text += '@all ';
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
text += `@${userId} `;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (seg.type === 'image') {
|
|
56
|
+
// 企业微信图片消息需要先上传获取 media_id,这里简化处理
|
|
57
|
+
if (seg.data.url || seg.data.file) {
|
|
58
|
+
text += `[图片: ${seg.data.url || seg.data.file}]`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
request.text = {
|
|
63
|
+
content: text,
|
|
64
|
+
};
|
|
65
|
+
const result = await bot.sendMessage(request);
|
|
66
|
+
return {
|
|
67
|
+
message_id: this.createId(result.msgid || result.response_code || Date.now().toString()),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 删除/撤回消息
|
|
72
|
+
*/
|
|
73
|
+
async deleteMessage(uin, params) {
|
|
74
|
+
// 企业微信不支持撤回消息
|
|
75
|
+
throw new Error('企业微信不支持撤回消息');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 获取消息
|
|
79
|
+
*/
|
|
80
|
+
async getMessage(uin, params) {
|
|
81
|
+
// 企业微信不支持直接获取消息
|
|
82
|
+
throw new Error('企业微信不支持直接获取消息');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 更新消息
|
|
86
|
+
*/
|
|
87
|
+
async updateMessage(uin, params) {
|
|
88
|
+
// 企业微信不支持更新消息
|
|
89
|
+
throw new Error('企业微信不支持更新消息');
|
|
90
|
+
}
|
|
91
|
+
// ============================================
|
|
92
|
+
// 用户相关方法
|
|
93
|
+
// ============================================
|
|
94
|
+
/**
|
|
95
|
+
* 获取机器人自身信息
|
|
96
|
+
*/
|
|
97
|
+
async getLoginInfo(uin) {
|
|
98
|
+
const account = this.getAccount(uin);
|
|
99
|
+
if (!account)
|
|
100
|
+
throw new Error(`Account ${uin} not found`);
|
|
101
|
+
const bot = account.client;
|
|
102
|
+
const me = bot.getCachedMe();
|
|
103
|
+
return {
|
|
104
|
+
user_id: this.createId(me?.userid || ''),
|
|
105
|
+
user_name: me?.name || '',
|
|
106
|
+
user_displayname: me?.name || '',
|
|
107
|
+
avatar: me?.avatar,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 获取用户信息
|
|
112
|
+
*/
|
|
113
|
+
async getUserInfo(uin, params) {
|
|
114
|
+
const account = this.getAccount(uin);
|
|
115
|
+
if (!account)
|
|
116
|
+
throw new Error(`Account ${uin} not found`);
|
|
117
|
+
const bot = account.client;
|
|
118
|
+
const userId = params.user_id.string;
|
|
119
|
+
const user = await bot.getUserInfo(userId);
|
|
120
|
+
return {
|
|
121
|
+
user_id: this.createId(user.userid),
|
|
122
|
+
user_name: user.name || '',
|
|
123
|
+
user_displayname: user.name || '',
|
|
124
|
+
avatar: user.avatar,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ============================================
|
|
128
|
+
// 好友(私聊会话)相关方法
|
|
129
|
+
// ============================================
|
|
130
|
+
/**
|
|
131
|
+
* 获取好友列表(企业微信不支持)
|
|
132
|
+
*/
|
|
133
|
+
async getFriendList(uin, params) {
|
|
134
|
+
// 企业微信不提供好友列表 API,可以通过部门成员列表获取
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 获取好友信息
|
|
139
|
+
*/
|
|
140
|
+
async getFriendInfo(uin, params) {
|
|
141
|
+
const account = this.getAccount(uin);
|
|
142
|
+
if (!account)
|
|
143
|
+
throw new Error(`Account ${uin} not found`);
|
|
144
|
+
const bot = account.client;
|
|
145
|
+
const userId = params.user_id.string;
|
|
146
|
+
const user = await bot.getUserInfo(userId);
|
|
147
|
+
return {
|
|
148
|
+
user_id: this.createId(user.userid),
|
|
149
|
+
user_name: user.name || '',
|
|
150
|
+
remark: user.alias || user.name || '',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// ============================================
|
|
154
|
+
// 群组相关方法
|
|
155
|
+
// ============================================
|
|
156
|
+
/**
|
|
157
|
+
* 获取群列表(部门列表)
|
|
158
|
+
*/
|
|
159
|
+
async getGroupList(uin, params) {
|
|
160
|
+
const account = this.getAccount(uin);
|
|
161
|
+
if (!account)
|
|
162
|
+
throw new Error(`Account ${uin} not found`);
|
|
163
|
+
const bot = account.client;
|
|
164
|
+
const departments = await bot.getDepartmentList();
|
|
165
|
+
return departments.map((dept) => ({
|
|
166
|
+
group_id: this.createId(dept.id.toString()),
|
|
167
|
+
group_name: dept.name || '',
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 获取群信息(部门信息)
|
|
172
|
+
*/
|
|
173
|
+
async getGroupInfo(uin, params) {
|
|
174
|
+
const account = this.getAccount(uin);
|
|
175
|
+
if (!account)
|
|
176
|
+
throw new Error(`Account ${uin} not found`);
|
|
177
|
+
const bot = account.client;
|
|
178
|
+
const deptId = parseInt(params.group_id.string);
|
|
179
|
+
const departments = await bot.getDepartmentList(deptId);
|
|
180
|
+
const dept = departments.find(d => d.id === deptId);
|
|
181
|
+
if (!dept) {
|
|
182
|
+
throw new Error(`部门 ${deptId} 不存在`);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
group_id: this.createId(dept.id.toString()),
|
|
186
|
+
group_name: dept.name || '',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 退出群组(企业微信不支持)
|
|
191
|
+
*/
|
|
192
|
+
async leaveGroup(uin, params) {
|
|
193
|
+
// 企业微信不支持退出部门
|
|
194
|
+
throw new Error('企业微信不支持退出部门');
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 获取群成员列表(部门成员列表)
|
|
198
|
+
*/
|
|
199
|
+
async getGroupMemberList(uin, params) {
|
|
200
|
+
const account = this.getAccount(uin);
|
|
201
|
+
if (!account)
|
|
202
|
+
throw new Error(`Account ${uin} not found`);
|
|
203
|
+
const bot = account.client;
|
|
204
|
+
const deptId = parseInt(params.group_id.string);
|
|
205
|
+
const members = await bot.getDepartmentMembers(deptId);
|
|
206
|
+
return members.map((user) => ({
|
|
207
|
+
group_id: params.group_id,
|
|
208
|
+
user_id: this.createId(user.userid),
|
|
209
|
+
user_name: user.name || '',
|
|
210
|
+
card: user.alias || user.name || '',
|
|
211
|
+
role: user.is_leader_in_dept?.some((isLeader, idx) => isLeader === 1 && user.department?.[idx] === deptId) ? 'admin' : 'member',
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 获取群成员信息
|
|
216
|
+
*/
|
|
217
|
+
async getGroupMemberInfo(uin, params) {
|
|
218
|
+
const account = this.getAccount(uin);
|
|
219
|
+
if (!account)
|
|
220
|
+
throw new Error(`Account ${uin} not found`);
|
|
221
|
+
const bot = account.client;
|
|
222
|
+
const userId = params.user_id.string;
|
|
223
|
+
const user = await bot.getUserInfo(userId);
|
|
224
|
+
const deptId = parseInt(params.group_id.string);
|
|
225
|
+
return {
|
|
226
|
+
group_id: params.group_id,
|
|
227
|
+
user_id: this.createId(user.userid),
|
|
228
|
+
user_name: user.name || '',
|
|
229
|
+
card: user.alias || user.name || '',
|
|
230
|
+
role: user.is_leader_in_dept?.some((isLeader, idx) => isLeader === 1 && user.department?.[idx] === deptId) ? 'admin' : 'member',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* 踢出群成员(企业微信不支持)
|
|
235
|
+
*/
|
|
236
|
+
async kickGroupMember(uin, params) {
|
|
237
|
+
// 企业微信不支持踢出部门成员
|
|
238
|
+
throw new Error('企业微信不支持踢出部门成员');
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 设置群名片(企业微信不支持)
|
|
242
|
+
*/
|
|
243
|
+
async setGroupCard(uin, params) {
|
|
244
|
+
// 企业微信不支持设置群名片
|
|
245
|
+
throw new Error('企业微信不支持设置群名片');
|
|
246
|
+
}
|
|
247
|
+
// ============================================
|
|
248
|
+
// 系统相关方法
|
|
249
|
+
// ============================================
|
|
250
|
+
/**
|
|
251
|
+
* 获取版本信息
|
|
252
|
+
*/
|
|
253
|
+
async getVersion(uin) {
|
|
254
|
+
return {
|
|
255
|
+
app_name: 'onebots 企业微信 Adapter',
|
|
256
|
+
app_version: '1.0.0',
|
|
257
|
+
impl: 'wecom',
|
|
258
|
+
version: '1.0.0',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 获取运行状态
|
|
263
|
+
*/
|
|
264
|
+
async getStatus(uin) {
|
|
265
|
+
const account = this.getAccount(uin);
|
|
266
|
+
return {
|
|
267
|
+
online: account?.status === AccountStatus.Online,
|
|
268
|
+
good: account?.status === AccountStatus.Online,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
// ============================================
|
|
272
|
+
// 账号创建
|
|
273
|
+
// ============================================
|
|
274
|
+
createAccount(config) {
|
|
275
|
+
const wecomConfig = {
|
|
276
|
+
account_id: config.account_id,
|
|
277
|
+
corp_id: config.corp_id,
|
|
278
|
+
corp_secret: config.corp_secret,
|
|
279
|
+
agent_id: config.agent_id,
|
|
280
|
+
token: config.token,
|
|
281
|
+
encoding_aes_key: config.encoding_aes_key,
|
|
282
|
+
};
|
|
283
|
+
const bot = new WeComBot(wecomConfig);
|
|
284
|
+
const account = new Account(this, bot, config);
|
|
285
|
+
// Webhook 路由(事件回调)
|
|
286
|
+
this.app.router.post(`${account.path}/webhook`, bot.handleWebhook.bind(bot));
|
|
287
|
+
// 监听 Bot 事件
|
|
288
|
+
bot.on('ready', () => {
|
|
289
|
+
this.logger.info(`企业微信 Bot ${config.account_id} 已就绪`);
|
|
290
|
+
});
|
|
291
|
+
bot.on('error', (error) => {
|
|
292
|
+
this.logger.error(`企业微信 Bot ${config.account_id} 错误:`, error);
|
|
293
|
+
});
|
|
294
|
+
// 监听企业微信事件
|
|
295
|
+
bot.on('event', (event) => {
|
|
296
|
+
this.handleWeComEvent(account, event);
|
|
297
|
+
});
|
|
298
|
+
// 启动时初始化 Bot
|
|
299
|
+
account.on('start', async () => {
|
|
300
|
+
try {
|
|
301
|
+
await bot.start();
|
|
302
|
+
account.status = AccountStatus.Online;
|
|
303
|
+
const me = bot.getCachedMe();
|
|
304
|
+
account.nickname = me?.name || '企业微信 Bot';
|
|
305
|
+
account.avatar = me?.avatar || this.icon;
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
this.logger.error(`启动企业微信 Bot 失败:`, error);
|
|
309
|
+
account.status = AccountStatus.OffLine;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
account.on('stop', async () => {
|
|
313
|
+
await bot.stop();
|
|
314
|
+
account.status = AccountStatus.OffLine;
|
|
315
|
+
});
|
|
316
|
+
return account;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* 处理企业微信事件
|
|
320
|
+
*/
|
|
321
|
+
handleWeComEvent(account, event) {
|
|
322
|
+
const eventType = event.EventType;
|
|
323
|
+
// 处理消息事件
|
|
324
|
+
if (eventType === 'change_contact' && event.ChangeType === 'create_user') {
|
|
325
|
+
// 用户创建事件
|
|
326
|
+
this.logger.info(`用户创建: ${event.UserID}`);
|
|
327
|
+
}
|
|
328
|
+
else if (eventType === 'change_contact' && event.ChangeType === 'update_user') {
|
|
329
|
+
// 用户更新事件
|
|
330
|
+
this.logger.info(`用户更新: ${event.UserID}`);
|
|
331
|
+
}
|
|
332
|
+
else if (eventType === 'change_contact' && event.ChangeType === 'delete_user') {
|
|
333
|
+
// 用户删除事件
|
|
334
|
+
this.logger.info(`用户删除: ${event.UserID}`);
|
|
335
|
+
}
|
|
336
|
+
// 注意:企业微信的消息事件需要通过其他方式接收(如应用消息回调)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
AdapterRegistry.register('wecom', WeComAdapter, {
|
|
340
|
+
name: 'wecom',
|
|
341
|
+
displayName: '企业微信官方机器人',
|
|
342
|
+
description: '企业微信官方机器人适配器,支持应用消息推送、通讯录同步',
|
|
343
|
+
icon: 'https://work.weixin.qq.com/favicon.ico',
|
|
344
|
+
homepage: 'https://work.weixin.qq.com/',
|
|
345
|
+
author: '凉菜',
|
|
346
|
+
});
|
|
347
|
+
//# sourceMappingURL=adapter.js.map
|
package/lib/bot.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信 Bot 客户端
|
|
3
|
+
* 基于企业微信开放平台 API,使用 fetch 实现
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
import type { RouterContext, Next } from 'onebots';
|
|
7
|
+
import type { WeComConfig, WeComSendMessageRequest, WeComSendMessageResponse, WeComUser, WeComDepartment } from './types.js';
|
|
8
|
+
export declare class WeComBot extends EventEmitter {
|
|
9
|
+
private config;
|
|
10
|
+
private accessToken;
|
|
11
|
+
private tokenExpireTime;
|
|
12
|
+
private me;
|
|
13
|
+
constructor(config: WeComConfig);
|
|
14
|
+
/**
|
|
15
|
+
* 发送 HTTP 请求
|
|
16
|
+
*/
|
|
17
|
+
private request;
|
|
18
|
+
/**
|
|
19
|
+
* GET 请求
|
|
20
|
+
*/
|
|
21
|
+
get<T = any>(path: string, params?: Record<string, string | number | boolean>): Promise<{
|
|
22
|
+
data: T;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* POST 请求
|
|
26
|
+
*/
|
|
27
|
+
post<T = any>(path: string, body?: any, params?: Record<string, string | number | boolean>): Promise<{
|
|
28
|
+
data: T;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* 获取访问令牌
|
|
32
|
+
*/
|
|
33
|
+
getAccessToken(): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* 启动 Bot
|
|
36
|
+
*/
|
|
37
|
+
start(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* 停止 Bot
|
|
40
|
+
*/
|
|
41
|
+
stop(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* 处理 Webhook 请求(事件回调)
|
|
44
|
+
*/
|
|
45
|
+
handleWebhook(ctx: RouterContext, next: Next): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* 获取缓存的 Bot 信息
|
|
48
|
+
*/
|
|
49
|
+
getCachedMe(): WeComUser | null;
|
|
50
|
+
/**
|
|
51
|
+
* 发送应用消息
|
|
52
|
+
*/
|
|
53
|
+
sendMessage(request: WeComSendMessageRequest): Promise<WeComSendMessageResponse>;
|
|
54
|
+
/**
|
|
55
|
+
* 获取用户信息
|
|
56
|
+
*/
|
|
57
|
+
getUserInfo(userId: string): Promise<WeComUser>;
|
|
58
|
+
/**
|
|
59
|
+
* 获取部门列表
|
|
60
|
+
*/
|
|
61
|
+
getDepartmentList(departmentId?: number): Promise<WeComDepartment[]>;
|
|
62
|
+
/**
|
|
63
|
+
* 获取部门成员列表
|
|
64
|
+
*/
|
|
65
|
+
getDepartmentMembers(departmentId: number, fetchChild?: boolean): Promise<WeComUser[]>;
|
|
66
|
+
/**
|
|
67
|
+
* 获取 HTTP 客户端实例(返回 this 以便链式调用)
|
|
68
|
+
*/
|
|
69
|
+
getHttpClient(): WeComBot;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=bot.d.ts.map
|
package/lib/bot.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信 Bot 客户端
|
|
3
|
+
* 基于企业微信开放平台 API,使用 fetch 实现
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
const WECOM_API_BASE = 'https://qyapi.weixin.qq.com';
|
|
7
|
+
export class WeComBot extends EventEmitter {
|
|
8
|
+
config;
|
|
9
|
+
accessToken = '';
|
|
10
|
+
tokenExpireTime = 0;
|
|
11
|
+
me = null;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super();
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 发送 HTTP 请求
|
|
18
|
+
*/
|
|
19
|
+
async request(path, options = {}) {
|
|
20
|
+
const { method = 'GET', headers = {}, body, params, skipAuth = false } = options;
|
|
21
|
+
// 构建 URL
|
|
22
|
+
const url = new URL(`${WECOM_API_BASE}${path}`);
|
|
23
|
+
if (params) {
|
|
24
|
+
for (const [key, value] of Object.entries(params)) {
|
|
25
|
+
if (value !== undefined) {
|
|
26
|
+
url.searchParams.append(key, String(value));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// 自动添加 token(除了获取 token 的请求)
|
|
31
|
+
if (!skipAuth && !path.includes('/gettoken')) {
|
|
32
|
+
const token = await this.getAccessToken();
|
|
33
|
+
url.searchParams.append('access_token', token);
|
|
34
|
+
}
|
|
35
|
+
// 构建请求头
|
|
36
|
+
const requestHeaders = {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
...headers,
|
|
39
|
+
};
|
|
40
|
+
// 发送请求
|
|
41
|
+
const response = await fetch(url.toString(), {
|
|
42
|
+
method,
|
|
43
|
+
headers: requestHeaders,
|
|
44
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
45
|
+
});
|
|
46
|
+
return response.json();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* GET 请求
|
|
50
|
+
*/
|
|
51
|
+
async get(path, params) {
|
|
52
|
+
const data = await this.request(path, { params });
|
|
53
|
+
return { data };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* POST 请求
|
|
57
|
+
*/
|
|
58
|
+
async post(path, body, params) {
|
|
59
|
+
const data = await this.request(path, { method: 'POST', body, params });
|
|
60
|
+
return { data };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 获取访问令牌
|
|
64
|
+
*/
|
|
65
|
+
async getAccessToken() {
|
|
66
|
+
if (this.accessToken && Date.now() < this.tokenExpireTime) {
|
|
67
|
+
return this.accessToken;
|
|
68
|
+
}
|
|
69
|
+
const data = await this.request('/cgi-bin/gettoken', {
|
|
70
|
+
params: {
|
|
71
|
+
corpid: this.config.corp_id,
|
|
72
|
+
corpsecret: this.config.corp_secret,
|
|
73
|
+
},
|
|
74
|
+
skipAuth: true,
|
|
75
|
+
});
|
|
76
|
+
if (data.errcode !== 0) {
|
|
77
|
+
throw new Error(`获取访问令牌失败: ${data.errmsg}`);
|
|
78
|
+
}
|
|
79
|
+
this.accessToken = data.access_token || '';
|
|
80
|
+
this.tokenExpireTime = Date.now() + ((data.expires_in || 7200) - 60) * 1000; // 提前60秒刷新
|
|
81
|
+
return this.accessToken;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 启动 Bot
|
|
85
|
+
*/
|
|
86
|
+
async start() {
|
|
87
|
+
try {
|
|
88
|
+
// 获取访问令牌
|
|
89
|
+
await this.getAccessToken();
|
|
90
|
+
// 获取应用信息(企业微信没有直接的 getBotInfo API)
|
|
91
|
+
this.me = {
|
|
92
|
+
userid: this.config.agent_id,
|
|
93
|
+
name: 'WeCom Bot',
|
|
94
|
+
};
|
|
95
|
+
this.emit('ready');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
this.emit('error', error);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 停止 Bot
|
|
104
|
+
*/
|
|
105
|
+
async stop() {
|
|
106
|
+
this.emit('stopped');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 处理 Webhook 请求(事件回调)
|
|
110
|
+
*/
|
|
111
|
+
async handleWebhook(ctx, next) {
|
|
112
|
+
const body = ctx.request.body;
|
|
113
|
+
const query = ctx.query;
|
|
114
|
+
// 处理 URL 验证(企业微信首次配置 webhook 时会发送验证请求)
|
|
115
|
+
if (query.msg_signature && query.timestamp && query.nonce && query.echostr) {
|
|
116
|
+
// 这里需要验证签名,简化处理直接返回 echostr
|
|
117
|
+
ctx.body = query.echostr;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// 处理事件
|
|
121
|
+
if (body.EventType) {
|
|
122
|
+
const event = body;
|
|
123
|
+
this.emit('event', event);
|
|
124
|
+
}
|
|
125
|
+
ctx.body = { code: 0 };
|
|
126
|
+
await next();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 获取缓存的 Bot 信息
|
|
130
|
+
*/
|
|
131
|
+
getCachedMe() {
|
|
132
|
+
return this.me;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 发送应用消息
|
|
136
|
+
*/
|
|
137
|
+
async sendMessage(request) {
|
|
138
|
+
const response = await this.post('/cgi-bin/message/send', {
|
|
139
|
+
...request,
|
|
140
|
+
agentid: parseInt(this.config.agent_id),
|
|
141
|
+
});
|
|
142
|
+
if (response.data.errcode !== 0) {
|
|
143
|
+
throw new Error(`发送消息失败: ${response.data.errmsg}`);
|
|
144
|
+
}
|
|
145
|
+
return response.data;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 获取用户信息
|
|
149
|
+
*/
|
|
150
|
+
async getUserInfo(userId) {
|
|
151
|
+
const response = await this.get('/cgi-bin/user/get', { userid: userId });
|
|
152
|
+
if (response.data.errcode !== 0) {
|
|
153
|
+
throw new Error(`获取用户信息失败: ${response.data.errmsg}`);
|
|
154
|
+
}
|
|
155
|
+
return response.data;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 获取部门列表
|
|
159
|
+
*/
|
|
160
|
+
async getDepartmentList(departmentId) {
|
|
161
|
+
const params = {};
|
|
162
|
+
if (departmentId !== undefined) {
|
|
163
|
+
params.id = departmentId;
|
|
164
|
+
}
|
|
165
|
+
const response = await this.get('/cgi-bin/department/list', params);
|
|
166
|
+
if (response.data.errcode !== 0) {
|
|
167
|
+
throw new Error(`获取部门列表失败: ${response.data.errmsg}`);
|
|
168
|
+
}
|
|
169
|
+
return response.data.department || [];
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 获取部门成员列表
|
|
173
|
+
*/
|
|
174
|
+
async getDepartmentMembers(departmentId, fetchChild) {
|
|
175
|
+
const response = await this.get('/cgi-bin/user/list', {
|
|
176
|
+
department_id: departmentId,
|
|
177
|
+
fetch_child: fetchChild ? 1 : 0,
|
|
178
|
+
});
|
|
179
|
+
if (response.data.errcode !== 0) {
|
|
180
|
+
throw new Error(`获取部门成员列表失败: ${response.data.errmsg}`);
|
|
181
|
+
}
|
|
182
|
+
return response.data.userlist || [];
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 获取 HTTP 客户端实例(返回 this 以便链式调用)
|
|
186
|
+
*/
|
|
187
|
+
getHttpClient() {
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=bot.js.map
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信开放平台 API 类型定义
|
|
3
|
+
* 基于企业微信开放平台官方 API
|
|
4
|
+
*/
|
|
5
|
+
export interface WeComConfig {
|
|
6
|
+
account_id: string;
|
|
7
|
+
corp_id: string;
|
|
8
|
+
corp_secret: string;
|
|
9
|
+
agent_id: string;
|
|
10
|
+
token?: string;
|
|
11
|
+
encoding_aes_key?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WeComUser {
|
|
14
|
+
userid: string;
|
|
15
|
+
name: string;
|
|
16
|
+
alias?: string;
|
|
17
|
+
mobile?: string;
|
|
18
|
+
department?: number[];
|
|
19
|
+
order?: number[];
|
|
20
|
+
position?: string;
|
|
21
|
+
gender?: string;
|
|
22
|
+
email?: string;
|
|
23
|
+
avatar?: string;
|
|
24
|
+
status?: number;
|
|
25
|
+
is_leader_in_dept?: number[];
|
|
26
|
+
telephone?: string;
|
|
27
|
+
address?: string;
|
|
28
|
+
extattr?: any;
|
|
29
|
+
to_invite?: boolean;
|
|
30
|
+
external_position?: string;
|
|
31
|
+
external_profile?: any;
|
|
32
|
+
}
|
|
33
|
+
export interface WeComDepartment {
|
|
34
|
+
id: number;
|
|
35
|
+
name: string;
|
|
36
|
+
name_en?: string;
|
|
37
|
+
parentid?: number;
|
|
38
|
+
order?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface WeComMessage {
|
|
41
|
+
msgid: string;
|
|
42
|
+
action: string;
|
|
43
|
+
from: {
|
|
44
|
+
type: string;
|
|
45
|
+
id: string;
|
|
46
|
+
};
|
|
47
|
+
tolist: string[];
|
|
48
|
+
roomid?: string;
|
|
49
|
+
msgtime: number;
|
|
50
|
+
msgtype: string;
|
|
51
|
+
text?: {
|
|
52
|
+
content: string;
|
|
53
|
+
};
|
|
54
|
+
image?: {
|
|
55
|
+
md5: string;
|
|
56
|
+
filesize: number;
|
|
57
|
+
sdkfileid: string;
|
|
58
|
+
};
|
|
59
|
+
voice?: {
|
|
60
|
+
md5: string;
|
|
61
|
+
voice_size: number;
|
|
62
|
+
play_length: number;
|
|
63
|
+
sdkfileid: string;
|
|
64
|
+
};
|
|
65
|
+
video?: {
|
|
66
|
+
md5: string;
|
|
67
|
+
filesize: number;
|
|
68
|
+
play_length: number;
|
|
69
|
+
sdkfileid: string;
|
|
70
|
+
};
|
|
71
|
+
file?: {
|
|
72
|
+
md5: string;
|
|
73
|
+
filename: string;
|
|
74
|
+
filesize: number;
|
|
75
|
+
sdkfileid: string;
|
|
76
|
+
};
|
|
77
|
+
[key: string]: any;
|
|
78
|
+
}
|
|
79
|
+
export interface WeComEvent {
|
|
80
|
+
EventType: string;
|
|
81
|
+
EventId: string;
|
|
82
|
+
TimeStamp: number;
|
|
83
|
+
FromUserName?: string;
|
|
84
|
+
ToUserName?: string;
|
|
85
|
+
AgentID?: string;
|
|
86
|
+
[key: string]: any;
|
|
87
|
+
}
|
|
88
|
+
export interface WeComTokenResponse {
|
|
89
|
+
errcode: number;
|
|
90
|
+
errmsg: string;
|
|
91
|
+
access_token?: string;
|
|
92
|
+
expires_in?: number;
|
|
93
|
+
}
|
|
94
|
+
export interface WeComSendMessageRequest {
|
|
95
|
+
touser?: string;
|
|
96
|
+
toparty?: string;
|
|
97
|
+
totag?: string;
|
|
98
|
+
msgtype: string;
|
|
99
|
+
agentid: number;
|
|
100
|
+
text?: {
|
|
101
|
+
content: string;
|
|
102
|
+
};
|
|
103
|
+
image?: {
|
|
104
|
+
media_id: string;
|
|
105
|
+
};
|
|
106
|
+
voice?: {
|
|
107
|
+
media_id: string;
|
|
108
|
+
};
|
|
109
|
+
video?: {
|
|
110
|
+
media_id: string;
|
|
111
|
+
title?: string;
|
|
112
|
+
description?: string;
|
|
113
|
+
};
|
|
114
|
+
file?: {
|
|
115
|
+
media_id: string;
|
|
116
|
+
};
|
|
117
|
+
textcard?: {
|
|
118
|
+
title: string;
|
|
119
|
+
description: string;
|
|
120
|
+
url: string;
|
|
121
|
+
btntxt?: string;
|
|
122
|
+
};
|
|
123
|
+
news?: {
|
|
124
|
+
articles: Array<{
|
|
125
|
+
title: string;
|
|
126
|
+
description?: string;
|
|
127
|
+
url: string;
|
|
128
|
+
picurl?: string;
|
|
129
|
+
}>;
|
|
130
|
+
};
|
|
131
|
+
mpnews?: {
|
|
132
|
+
articles: Array<{
|
|
133
|
+
title: string;
|
|
134
|
+
thumb_media_id: string;
|
|
135
|
+
author?: string;
|
|
136
|
+
content_source_url?: string;
|
|
137
|
+
content: string;
|
|
138
|
+
digest?: string;
|
|
139
|
+
}>;
|
|
140
|
+
};
|
|
141
|
+
markdown?: {
|
|
142
|
+
content: string;
|
|
143
|
+
};
|
|
144
|
+
[key: string]: any;
|
|
145
|
+
}
|
|
146
|
+
export interface WeComSendMessageResponse {
|
|
147
|
+
errcode: number;
|
|
148
|
+
errmsg: string;
|
|
149
|
+
invaliduser?: string;
|
|
150
|
+
invalidparty?: string;
|
|
151
|
+
invalidtag?: string;
|
|
152
|
+
msgid?: string;
|
|
153
|
+
response_code?: string;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=types.d.ts.map
|
package/lib/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onebots/adapter-wecom",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "onebots 企业微信适配器",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"onebots",
|
|
10
|
+
"wecom",
|
|
11
|
+
"wechat-work",
|
|
12
|
+
"adapter"
|
|
13
|
+
],
|
|
14
|
+
"author": "凉菜",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public",
|
|
18
|
+
"registry": "https://registry.npmjs.org"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"/lib/**/*.js",
|
|
22
|
+
"/lib/**/*.d.ts"
|
|
23
|
+
],
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsc-alias": "latest",
|
|
26
|
+
"typescript": "latest",
|
|
27
|
+
"@types/node": "^22.7.3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"onebots": "1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json",
|
|
35
|
+
"clean": "rm -rf lib *.tsbuildinfo"
|
|
36
|
+
}
|
|
37
|
+
}
|