@zhin.js/adapter-milky 1.0.1 → 1.1.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/CHANGELOG.md +1046 -0
- package/{skills/milky/PERMITS.md → PERMITS.md} +2 -2
- package/README.md +60 -117
- package/adapters/milky/index.js +51 -0
- package/adapters/milky/index.ts +74 -0
- package/commands/milky/endpoint/add/[id]/index.js +3 -0
- package/commands/milky/endpoint/add/[id]/index.ts +3 -0
- package/commands/milky/endpoint/definition.js +19 -0
- package/commands/milky/endpoint/definition.ts +19 -0
- package/commands/milky/endpoint/list/index.js +3 -0
- package/commands/milky/endpoint/list/index.ts +3 -0
- package/commands/milky/endpoint/remove/[id]/index.js +3 -0
- package/commands/milky/endpoint/remove/[id]/index.ts +3 -0
- package/lib/client.d.ts +21 -0
- package/lib/client.js +33 -0
- package/lib/endpoint-management.d.ts +17 -0
- package/lib/endpoint-management.js +47 -0
- package/lib/endpoint.d.ts +7 -0
- package/lib/endpoint.js +6 -0
- package/lib/index.d.ts +6 -20
- package/lib/index.js +4 -35
- package/lib/milky-auth.d.ts +2 -0
- package/lib/milky-auth.js +16 -0
- package/lib/milky-runtime-state.d.ts +1 -0
- package/lib/milky-runtime-state.js +6 -0
- package/lib/protocol.d.ts +160 -0
- package/lib/protocol.js +491 -0
- package/lib/sse-client.d.ts +20 -0
- package/lib/sse-client.js +85 -0
- package/lib/sse-endpoint.d.ts +32 -0
- package/lib/sse-endpoint.js +207 -0
- package/lib/webhook-endpoint.d.ts +22 -0
- package/lib/webhook-endpoint.js +149 -0
- package/lib/ws-endpoint.d.ts +23 -0
- package/lib/ws-endpoint.js +233 -0
- package/lib/ws-types.d.ts +11 -0
- package/lib/ws-types.js +1 -0
- package/lib/wss-endpoint.d.ts +25 -0
- package/lib/wss-endpoint.js +199 -0
- package/package.json +78 -19
- package/plugin.js +14 -0
- package/schema.json +119 -0
- package/src/client.ts +80 -0
- package/src/endpoint-management.ts +74 -0
- package/src/endpoint.ts +26 -0
- package/src/index.ts +62 -47
- package/src/milky-auth.ts +14 -0
- package/src/milky-runtime-state.ts +7 -0
- package/src/protocol.ts +633 -0
- package/src/sse-client.ts +106 -0
- package/src/sse-endpoint.ts +258 -0
- package/src/webhook-endpoint.ts +196 -0
- package/src/ws-endpoint.ts +291 -0
- package/src/ws-types.ts +12 -0
- package/src/wss-endpoint.ts +241 -0
- package/lib/adapter.d.ts +0 -29
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -91
- package/lib/adapter.js.map +0 -1
- package/lib/api.d.ts +0 -10
- package/lib/api.d.ts.map +0 -1
- package/lib/api.js +0 -48
- package/lib/api.js.map +0 -1
- package/lib/endpoint-sse.d.ts +0 -31
- package/lib/endpoint-sse.d.ts.map +0 -1
- package/lib/endpoint-sse.js +0 -195
- package/lib/endpoint-sse.js.map +0 -1
- package/lib/endpoint-webhook.d.ts +0 -34
- package/lib/endpoint-webhook.d.ts.map +0 -1
- package/lib/endpoint-webhook.js +0 -187
- package/lib/endpoint-webhook.js.map +0 -1
- package/lib/endpoint-ws.d.ts +0 -35
- package/lib/endpoint-ws.d.ts.map +0 -1
- package/lib/endpoint-ws.js +0 -262
- package/lib/endpoint-ws.js.map +0 -1
- package/lib/endpoint-wss.d.ts +0 -34
- package/lib/endpoint-wss.d.ts.map +0 -1
- package/lib/endpoint-wss.js +0 -225
- package/lib/endpoint-wss.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types.d.ts +0 -75
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -5
- package/lib/types.js.map +0 -1
- package/lib/utils.d.ts +0 -35
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js +0 -165
- package/lib/utils.js.map +0 -1
- package/plugin.yml +0 -3
- package/skills/milky/SKILL.md +0 -65
- package/src/adapter.ts +0 -103
- package/src/api.ts +0 -63
- package/src/endpoint-sse.ts +0 -228
- package/src/endpoint-webhook.ts +0 -221
- package/src/endpoint-ws.ts +0 -296
- package/src/endpoint-wss.ts +0 -268
- package/src/types.ts +0 -73
- package/src/utils.ts +0 -181
package/lib/types.js
DELETED
package/lib/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/lib/utils.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Milky 事件/消息段与 zhin Message 的转换
|
|
3
|
-
*/
|
|
4
|
-
import type { MessageBase } from 'zhin.js';
|
|
5
|
-
import type { MilkyEvent, MilkyIncomingMessage, MilkyIncomingSegment } from './types.js';
|
|
6
|
-
/** 将 Milky 接收消息段转为 zhin 的 $content(MessageSegment[]) */
|
|
7
|
-
export declare function formatMilkySegments(segments: MilkyIncomingSegment[]): Array<{
|
|
8
|
-
type: string;
|
|
9
|
-
data: Record<string, unknown>;
|
|
10
|
-
}>;
|
|
11
|
-
/** message_receive 事件的 data 转为 zhin Message 的构造参数(含 $adapter、$endpoint) */
|
|
12
|
-
export declare function formatMilkyMessagePayload(event: MilkyEvent, data: MilkyIncomingMessage, recallFn: (msgId: string) => Promise<void>, replyFn: (channel: {
|
|
13
|
-
id: string;
|
|
14
|
-
type: 'group' | 'private';
|
|
15
|
-
}, content: (string | {
|
|
16
|
-
type: string;
|
|
17
|
-
data?: Record<string, unknown>;
|
|
18
|
-
})[], quote?: boolean | string) => Promise<string>, adapterName: 'milky', endpointName: string): MessageBase;
|
|
19
|
-
/** 根据 event_type 判断是否为 message_receive,并解析 data 为 MilkyIncomingMessage */
|
|
20
|
-
export declare function parseMessageReceiveData(event: MilkyEvent): MilkyIncomingMessage | null;
|
|
21
|
-
/** zhin SendContent(string | MessageElement 或数组)转为 Milky OutgoingSegment[](发送用) */
|
|
22
|
-
export declare function toMilkyOutgoingSegments(content: (string | {
|
|
23
|
-
type: string;
|
|
24
|
-
data?: Record<string, unknown>;
|
|
25
|
-
})[]): Array<{
|
|
26
|
-
type: string;
|
|
27
|
-
data: Record<string, unknown>;
|
|
28
|
-
}>;
|
|
29
|
-
/** 解析 $id 为 message_scene、peer_id、message_seq(用于撤回) */
|
|
30
|
-
export declare function parseMilkyMessageId(msgId: string): {
|
|
31
|
-
message_scene: 'friend' | 'group' | 'temp';
|
|
32
|
-
peer_id: number;
|
|
33
|
-
message_seq: number;
|
|
34
|
-
} | null;
|
|
35
|
-
//# sourceMappingURL=utils.d.ts.map
|
package/lib/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEzF,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAmD5H;AAED,2EAA2E;AAC3E,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,oBAAoB,EAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAC1C,OAAO,EAAE,CAAC,OAAO,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,EAAE,OAAO,EAAE,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAClL,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,MAAM,GACnB,WAAW,CAgCb;AAED,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,GAAG,oBAAoB,GAAG,IAAI,CAKtF;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAAE,GACrE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAoDxD;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GACZ;IAAE,aAAa,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAS7F"}
|
package/lib/utils.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { applyQqSenderRoleToMessageSender } from 'zhin.js';
|
|
2
|
-
/** 将 Milky 接收消息段转为 zhin 的 $content(MessageSegment[]) */
|
|
3
|
-
export function formatMilkySegments(segments) {
|
|
4
|
-
return segments.map((seg) => {
|
|
5
|
-
const type = seg.type;
|
|
6
|
-
const data = seg.data ?? {};
|
|
7
|
-
switch (type) {
|
|
8
|
-
case 'text':
|
|
9
|
-
return { type: 'text', data: { text: data.text ?? '' } };
|
|
10
|
-
case 'mention':
|
|
11
|
-
return { type: 'at', data: { id: String(data.user_id ?? '') } };
|
|
12
|
-
case 'mention_all':
|
|
13
|
-
return { type: 'at', data: { type: 'all' } };
|
|
14
|
-
case 'face':
|
|
15
|
-
return { type: 'face', data: { id: data.face_id ?? '' } };
|
|
16
|
-
case 'reply':
|
|
17
|
-
return {
|
|
18
|
-
type: 'reply',
|
|
19
|
-
data: { message_seq: data.message_seq },
|
|
20
|
-
};
|
|
21
|
-
case 'image':
|
|
22
|
-
return {
|
|
23
|
-
type: 'image',
|
|
24
|
-
data: {
|
|
25
|
-
url: data.temp_url ?? data.resource_id ?? '',
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
case 'record':
|
|
29
|
-
return {
|
|
30
|
-
type: 'record',
|
|
31
|
-
data: {
|
|
32
|
-
url: data.temp_url ?? data.resource_id ?? '',
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
case 'video':
|
|
36
|
-
return {
|
|
37
|
-
type: 'video',
|
|
38
|
-
data: {
|
|
39
|
-
url: data.temp_url ?? data.resource_id ?? '',
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
case 'file':
|
|
43
|
-
return {
|
|
44
|
-
type: 'file',
|
|
45
|
-
data: {
|
|
46
|
-
file_id: data.file_id,
|
|
47
|
-
name: data.file_name,
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
default:
|
|
51
|
-
return { type, data: data };
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
/** message_receive 事件的 data 转为 zhin Message 的构造参数(含 $adapter、$endpoint) */
|
|
56
|
-
export function formatMilkyMessagePayload(event, data, recallFn, replyFn, adapterName, endpointName) {
|
|
57
|
-
const scene = data.message_scene;
|
|
58
|
-
const isGroup = scene === 'group';
|
|
59
|
-
const channelId = data.peer_id.toString();
|
|
60
|
-
const msgId = `${data.message_scene}:${data.peer_id}:${data.message_seq}`;
|
|
61
|
-
const channel = { id: channelId, type: (isGroup ? 'group' : 'private') };
|
|
62
|
-
const senderId = data.sender_id.toString();
|
|
63
|
-
const senderName = (isGroup ? data.group_member?.card ?? data.group_member?.nickname : data.friend?.nickname) ?? senderId;
|
|
64
|
-
const content = formatMilkySegments(data.segments);
|
|
65
|
-
const raw = content.map((c) => (c.type === 'text' ? c.data.text : '')).join('');
|
|
66
|
-
return {
|
|
67
|
-
$id: msgId,
|
|
68
|
-
$adapter: 'milky',
|
|
69
|
-
$endpoint: endpointName,
|
|
70
|
-
$channel: channel,
|
|
71
|
-
$sender: (() => {
|
|
72
|
-
const sender = {
|
|
73
|
-
id: senderId,
|
|
74
|
-
name: senderName,
|
|
75
|
-
};
|
|
76
|
-
applyQqSenderRoleToMessageSender(sender, data.group_member?.role);
|
|
77
|
-
return sender;
|
|
78
|
-
})(),
|
|
79
|
-
$content: content,
|
|
80
|
-
$raw: raw,
|
|
81
|
-
$timestamp: data.time,
|
|
82
|
-
$recall: () => recallFn(msgId),
|
|
83
|
-
$reply: (cnt, quote) => replyFn(channel, (Array.isArray(cnt) ? cnt : [cnt]), quote),
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/** 根据 event_type 判断是否为 message_receive,并解析 data 为 MilkyIncomingMessage */
|
|
87
|
-
export function parseMessageReceiveData(event) {
|
|
88
|
-
if (event.event_type !== 'message_receive' || !event.data)
|
|
89
|
-
return null;
|
|
90
|
-
const data = event.data;
|
|
91
|
-
if (!data.message_scene || !Number.isInteger(data.peer_id) || !Array.isArray(data.segments))
|
|
92
|
-
return null;
|
|
93
|
-
return data;
|
|
94
|
-
}
|
|
95
|
-
/** zhin SendContent(string | MessageElement 或数组)转为 Milky OutgoingSegment[](发送用) */
|
|
96
|
-
export function toMilkyOutgoingSegments(content) {
|
|
97
|
-
const out = [];
|
|
98
|
-
for (const seg of content) {
|
|
99
|
-
const type = typeof seg === 'string' ? 'text' : seg.type;
|
|
100
|
-
const data = typeof seg === 'string' ? { text: seg } : (seg.data ?? {});
|
|
101
|
-
switch (type) {
|
|
102
|
-
case 'text':
|
|
103
|
-
out.push({ type: 'text', data: { text: String(data.text ?? '') } });
|
|
104
|
-
break;
|
|
105
|
-
case 'at':
|
|
106
|
-
if (data.type === 'all') {
|
|
107
|
-
out.push({ type: 'mention_all', data: {} });
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
const id = data.id;
|
|
111
|
-
if (id)
|
|
112
|
-
out.push({ type: 'mention', data: { user_id: Number(id) || 0 } });
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
115
|
-
case 'face':
|
|
116
|
-
out.push({ type: 'face', data: { face_id: String(data.id ?? ''), is_large: false } });
|
|
117
|
-
break;
|
|
118
|
-
case 'reply':
|
|
119
|
-
// message_id 可能为 scene:peer:seq 或仅 message_seq
|
|
120
|
-
const mid = data.message_id ?? data.message_seq;
|
|
121
|
-
const seq = typeof mid === 'number' ? mid : parseInt(String(mid).split(':').pop() ?? '0', 10);
|
|
122
|
-
out.push({ type: 'reply', data: { message_seq: seq } });
|
|
123
|
-
break;
|
|
124
|
-
case 'image':
|
|
125
|
-
out.push({
|
|
126
|
-
type: 'image',
|
|
127
|
-
data: { uri: String(data.url ?? data.uri ?? '') },
|
|
128
|
-
});
|
|
129
|
-
break;
|
|
130
|
-
case 'record':
|
|
131
|
-
out.push({
|
|
132
|
-
type: 'record',
|
|
133
|
-
data: { uri: String(data.url ?? data.uri ?? '') },
|
|
134
|
-
});
|
|
135
|
-
break;
|
|
136
|
-
case 'video':
|
|
137
|
-
out.push({
|
|
138
|
-
type: 'video',
|
|
139
|
-
data: {
|
|
140
|
-
uri: String(data.url ?? data.uri ?? ''),
|
|
141
|
-
thumb_uri: data.thumb_uri,
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
break;
|
|
145
|
-
default:
|
|
146
|
-
out.push({ type, data: data });
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return out;
|
|
150
|
-
}
|
|
151
|
-
/** 解析 $id 为 message_scene、peer_id、message_seq(用于撤回) */
|
|
152
|
-
export function parseMilkyMessageId(msgId) {
|
|
153
|
-
const parts = msgId.split(':');
|
|
154
|
-
if (parts.length < 3)
|
|
155
|
-
return null;
|
|
156
|
-
const [scene, peer, seq] = parts;
|
|
157
|
-
if (scene !== 'friend' && scene !== 'group' && scene !== 'temp')
|
|
158
|
-
return null;
|
|
159
|
-
const peerId = parseInt(peer, 10);
|
|
160
|
-
const messageSeq = parseInt(seq, 10);
|
|
161
|
-
if (Number.isNaN(peerId) || Number.isNaN(messageSeq))
|
|
162
|
-
return null;
|
|
163
|
-
return { message_scene: scene, peer_id: peerId, message_seq: messageSeq };
|
|
164
|
-
}
|
|
165
|
-
//# sourceMappingURL=utils.js.map
|
package/lib/utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/plugin.yml
DELETED
package/skills/milky/SKILL.md
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: milky
|
|
3
|
-
platforms:
|
|
4
|
-
- milky
|
|
5
|
-
description: >-
|
|
6
|
-
Milky 协议群管理能力。当用户在 Milky 协议的 QQ 群中请求踢人、禁言、设管理员、
|
|
7
|
-
改名片/群名或查成员/群信息时使用。即使用户没有提到 Milky,只要 Endpoint 平台是
|
|
8
|
-
Milky 协议且涉及群管理操作,就应触发。仅有昵称时必须先查成员列表获取 user_id。
|
|
9
|
-
keywords:
|
|
10
|
-
- milky
|
|
11
|
-
- adapter:milky
|
|
12
|
-
- 群管理
|
|
13
|
-
- 禁言
|
|
14
|
-
- 踢人
|
|
15
|
-
- 管理员
|
|
16
|
-
- list_members
|
|
17
|
-
tags:
|
|
18
|
-
- group
|
|
19
|
-
- management
|
|
20
|
-
- im
|
|
21
|
-
tools:
|
|
22
|
-
- milky_kick_member
|
|
23
|
-
- milky_mute_member
|
|
24
|
-
- milky_mute_all
|
|
25
|
-
- milky_set_admin
|
|
26
|
-
- milky_set_nickname
|
|
27
|
-
- milky_set_group_name
|
|
28
|
-
- milky_list_members
|
|
29
|
-
- milky_get_group_info
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
# Milky 群管理技能
|
|
33
|
-
|
|
34
|
-
Milky 是 QQ 生态中的一种协议实现,提供标准群管理工具集。
|
|
35
|
-
|
|
36
|
-
## 核心原则
|
|
37
|
-
|
|
38
|
-
### 先查后操作
|
|
39
|
-
|
|
40
|
-
只有昵称时,先 `milky_list_members` 获取 QQ 号再操作。
|
|
41
|
-
|
|
42
|
-
**Example:**
|
|
43
|
-
```
|
|
44
|
-
用户: 禁言那个叫"阿强"的
|
|
45
|
-
步骤1: milky_list_members → 找到 user_id = 654321
|
|
46
|
-
步骤2: milky_mute_member(user_id=654321, duration=600)
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## 工具详解
|
|
50
|
-
|
|
51
|
-
| 工具 | 用途 | 权限 | 关键参数 |
|
|
52
|
-
|------|------|------|----------|
|
|
53
|
-
| `milky_kick_member` | 踢出成员 | admin | `user_id` |
|
|
54
|
-
| `milky_mute_member` | 禁言/解禁 | admin | `user_id`, `duration`(秒,**0=解禁**) |
|
|
55
|
-
| `milky_mute_all` | 全员禁言/解除 | admin | `enable` |
|
|
56
|
-
| `milky_set_admin` | 设/取消管理员 | owner | `user_id`, `enable` |
|
|
57
|
-
| `milky_set_nickname` | 改群名片 | admin | `user_id`, `nickname` |
|
|
58
|
-
| `milky_set_group_name` | 改群名 | admin | `name` |
|
|
59
|
-
| `milky_list_members` | 成员列表 | user | — |
|
|
60
|
-
| `milky_get_group_info` | 群信息 | user | — |
|
|
61
|
-
|
|
62
|
-
## 易错点
|
|
63
|
-
|
|
64
|
-
1. **禁言 duration=0 是解禁**,不是永久禁言。
|
|
65
|
-
2. **管理员设置需要群主权限**(owner),管理员无法设置其他管理员。
|
package/src/adapter.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Milky 适配器:单一适配器支持 WS 正向 / SSE / Webhook / 反向 WS,由 config.connection 区分
|
|
3
|
-
*/
|
|
4
|
-
import { formatCompact, Adapter, Plugin, type IGroupManagement } from 'zhin.js';
|
|
5
|
-
import type { Router } from '@zhin.js/host-router';
|
|
6
|
-
import { MilkyWsClient } from './endpoint-ws.js';
|
|
7
|
-
import { MilkySseClient } from './endpoint-sse.js';
|
|
8
|
-
import { MilkyWebhookEndpoint } from './endpoint-webhook.js';
|
|
9
|
-
import { MilkyWssServer } from './endpoint-wss.js';
|
|
10
|
-
import type {
|
|
11
|
-
MilkyEndpointConfig,
|
|
12
|
-
MilkyWsConfig,
|
|
13
|
-
MilkySseConfig,
|
|
14
|
-
MilkyWebhookConfig,
|
|
15
|
-
MilkyWssConfig,
|
|
16
|
-
} from './types.js';
|
|
17
|
-
|
|
18
|
-
export type MilkyBot = MilkyWsClient | MilkySseClient | MilkyWebhookEndpoint | MilkyWssServer;
|
|
19
|
-
|
|
20
|
-
export class MilkyAdapter extends Adapter<MilkyBot> {
|
|
21
|
-
static override readonly capabilities = ['inbound', 'outbound'] as const;
|
|
22
|
-
|
|
23
|
-
#router?: Router;
|
|
24
|
-
|
|
25
|
-
constructor(plugin: Plugin) {
|
|
26
|
-
super(plugin, 'milky', []);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
createEndpoint(config: MilkyEndpointConfig): MilkyBot {
|
|
30
|
-
switch (config.connection) {
|
|
31
|
-
case 'ws':
|
|
32
|
-
return new MilkyWsClient(this, config as MilkyWsConfig);
|
|
33
|
-
case 'sse':
|
|
34
|
-
return new MilkySseClient(this, config as MilkySseConfig);
|
|
35
|
-
case 'webhook':
|
|
36
|
-
if (!this.#router) throw new Error('Milky connection: webhook 需要 router,请安装并在配置中启用 @zhin.js/host-router');
|
|
37
|
-
return new MilkyWebhookEndpoint(this, this.#router, config as MilkyWebhookConfig);
|
|
38
|
-
case 'wss':
|
|
39
|
-
if (!this.#router) throw new Error('Milky connection: wss 需要 router,请安装并在配置中启用 @zhin.js/host-router');
|
|
40
|
-
return new MilkyWssServer(this, this.#router, config as MilkyWssConfig);
|
|
41
|
-
default:
|
|
42
|
-
throw new Error(`Unknown Milky connection: ${(config as MilkyEndpointConfig).connection}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async kickMember(endpointId: string, sceneId: string, userId: string) {
|
|
47
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
48
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
49
|
-
return endpoint.kickMember(Number(sceneId), Number(userId), false);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async muteMember(endpointId: string, sceneId: string, userId: string, duration = 600) {
|
|
53
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
54
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
55
|
-
return endpoint.muteMember(Number(sceneId), Number(userId), duration);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async muteAll(endpointId: string, sceneId: string, enable = true) {
|
|
59
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
60
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
61
|
-
return endpoint.muteAll(Number(sceneId), enable);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async setAdmin(endpointId: string, sceneId: string, userId: string, enable = true) {
|
|
65
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
66
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
67
|
-
return endpoint.setAdmin(Number(sceneId), Number(userId), enable);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async setMemberNickname(endpointId: string, sceneId: string, userId: string, nickname: string) {
|
|
71
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
72
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
73
|
-
return endpoint.setCard(Number(sceneId), Number(userId), nickname);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async setGroupName(endpointId: string, sceneId: string, name: string) {
|
|
77
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
78
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
79
|
-
return endpoint.setGroupName(Number(sceneId), name);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async listMembers(endpointId: string, sceneId: string) {
|
|
83
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
84
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
85
|
-
const members = await endpoint.getMemberList(Number(sceneId));
|
|
86
|
-
return { members: Array.isArray(members) ? members : [], count: Array.isArray(members) ? members.length : 0 };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async getGroupInfo(endpointId: string, sceneId: string) {
|
|
90
|
-
const endpoint = this.endpoints.get(endpointId);
|
|
91
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
92
|
-
return endpoint.getGroupInfo(Number(sceneId));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async start(): Promise<void> {
|
|
96
|
-
// 同步获取已就绪的 router,或通过 useContext 在 router 挂载后赋值
|
|
97
|
-
this.#router = (this.plugin.inject as (key: string) => Router | undefined)('router');
|
|
98
|
-
(this.plugin.useContext as (key: string, fn: (router: Router) => void) => void)('router', (router: Router) => {
|
|
99
|
-
this.#router = router;
|
|
100
|
-
});
|
|
101
|
-
await super.start();
|
|
102
|
-
}
|
|
103
|
-
}
|
package/src/api.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Milky HTTP API 封装:鉴权 + POST /api/:api
|
|
3
|
-
*/
|
|
4
|
-
import type { MilkyApiResponse } from './types.js';
|
|
5
|
-
|
|
6
|
-
export interface MilkyApiClientOptions {
|
|
7
|
-
baseUrl: string;
|
|
8
|
-
access_token?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** 鉴权:Header Authorization: Bearer {token} 或 URL query access_token=xxx */
|
|
12
|
-
function authHeaders(access_token?: string): Record<string, string> {
|
|
13
|
-
const headers: Record<string, string> = {};
|
|
14
|
-
if (access_token) {
|
|
15
|
-
headers['Authorization'] = `Bearer ${access_token}`;
|
|
16
|
-
}
|
|
17
|
-
return headers;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function authQuery(access_token?: string): string {
|
|
21
|
-
if (!access_token) return '';
|
|
22
|
-
return `access_token=${encodeURIComponent(access_token)}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 调用协议端 API:POST {baseUrl}/api/{apiName},Body JSON,鉴权。
|
|
27
|
-
* 非 200 或 retcode !== 0 时抛错。
|
|
28
|
-
*/
|
|
29
|
-
export async function callApi<T = unknown>(
|
|
30
|
-
options: MilkyApiClientOptions,
|
|
31
|
-
apiName: string,
|
|
32
|
-
params: Record<string, unknown> = {},
|
|
33
|
-
): Promise<T> {
|
|
34
|
-
const { baseUrl, access_token } = options;
|
|
35
|
-
const url = new URL(`/api/${apiName}`, baseUrl.replace(/\/$/, ''));
|
|
36
|
-
const q = authQuery(access_token);
|
|
37
|
-
if (q) url.search = (url.search ? url.search + '&' : '') + q;
|
|
38
|
-
|
|
39
|
-
const res = await fetch(url.toString(), {
|
|
40
|
-
method: 'POST',
|
|
41
|
-
headers: {
|
|
42
|
-
'Content-Type': 'application/json',
|
|
43
|
-
...authHeaders(access_token),
|
|
44
|
-
},
|
|
45
|
-
body: JSON.stringify(Object.keys(params).length ? params : {}),
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const text = await res.text();
|
|
49
|
-
let body: MilkyApiResponse<T>;
|
|
50
|
-
try {
|
|
51
|
-
body = JSON.parse(text) as MilkyApiResponse<T>;
|
|
52
|
-
} catch {
|
|
53
|
-
throw new Error(`Milky API ${apiName}: invalid JSON response (${res.status}) ${text.slice(0, 200)}`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (res.status !== 200) {
|
|
57
|
-
throw new Error(`Milky API ${apiName}: HTTP ${res.status} ${body.message ?? text}`);
|
|
58
|
-
}
|
|
59
|
-
if (body.retcode !== 0) {
|
|
60
|
-
throw new Error(`Milky API ${apiName}: retcode=${body.retcode} ${body.message ?? ''}`);
|
|
61
|
-
}
|
|
62
|
-
return (body.data ?? {}) as T;
|
|
63
|
-
}
|