@zhin.js/adapter-kook 1.0.84 → 1.1.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +804 -17
  2. package/README.md +75 -374
  3. package/adapters/kook.js +41 -0
  4. package/adapters/kook.ts +56 -0
  5. package/agent/PERMITS.md +23 -0
  6. package/agent/tools/blacklist.ts +31 -0
  7. package/agent/tools/create_role.ts +23 -0
  8. package/agent/tools/delete_role.ts +19 -0
  9. package/agent/tools/grant_role.ts +20 -0
  10. package/agent/tools/list_roles.ts +25 -0
  11. package/agent/tools/revoke_role.ts +20 -0
  12. package/commands/endpoint/add/[id].js +3 -0
  13. package/commands/endpoint/add/[id].ts +3 -0
  14. package/commands/endpoint/list.js +3 -0
  15. package/commands/endpoint/list.ts +3 -0
  16. package/commands/endpoint/remove/[id].js +3 -0
  17. package/commands/endpoint/remove/[id].ts +3 -0
  18. package/lib/client.d.ts +11 -0
  19. package/lib/client.js +2 -0
  20. package/lib/endpoint.d.ts +59 -0
  21. package/lib/endpoint.js +374 -0
  22. package/lib/index.d.ts +6 -18
  23. package/lib/index.js +6 -342
  24. package/lib/kook-endpoint-commands.d.ts +1 -0
  25. package/lib/kook-endpoint-commands.js +18 -0
  26. package/lib/kook-runtime-state.d.ts +1 -0
  27. package/lib/kook-runtime-state.js +6 -0
  28. package/lib/platform-permit.d.ts +22 -0
  29. package/lib/platform-permit.js +48 -0
  30. package/lib/protocol.d.ts +144 -0
  31. package/lib/protocol.js +313 -0
  32. package/lib/side-event-dispatch.d.ts +3 -0
  33. package/lib/side-event-dispatch.js +49 -0
  34. package/lib/webhook.d.ts +16 -0
  35. package/lib/webhook.js +71 -0
  36. package/lib/ws.d.ts +56 -0
  37. package/lib/ws.js +63 -0
  38. package/package.json +60 -28
  39. package/plugin.js +19 -0
  40. package/schema.json +128 -0
  41. package/src/client.ts +15 -0
  42. package/src/endpoint.ts +433 -0
  43. package/src/index.ts +45 -322
  44. package/src/kook-endpoint-commands.ts +19 -0
  45. package/src/kook-runtime-state.ts +7 -0
  46. package/src/platform-permit.ts +75 -0
  47. package/src/protocol.ts +462 -0
  48. package/src/side-event-dispatch.ts +54 -0
  49. package/src/webhook.ts +114 -0
  50. package/src/ws.ts +131 -0
  51. package/client/Dashboard.tsx +0 -255
  52. package/client/index.tsx +0 -11
  53. package/client/tsconfig.json +0 -7
  54. package/client/utils/api.ts +0 -17
  55. package/dist/index.js +0 -31
  56. package/lib/adapter.d.ts +0 -26
  57. package/lib/adapter.d.ts.map +0 -1
  58. package/lib/adapter.js +0 -61
  59. package/lib/adapter.js.map +0 -1
  60. package/lib/bot.d.ts +0 -122
  61. package/lib/bot.d.ts.map +0 -1
  62. package/lib/bot.js +0 -608
  63. package/lib/bot.js.map +0 -1
  64. package/lib/index.d.ts.map +0 -1
  65. package/lib/index.js.map +0 -1
  66. package/lib/types.d.ts +0 -32
  67. package/lib/types.d.ts.map +0 -1
  68. package/lib/types.js +0 -8
  69. package/lib/types.js.map +0 -1
  70. package/plugin.yml +0 -3
  71. package/src/adapter.ts +0 -71
  72. package/src/bot.ts +0 -693
  73. package/src/types.ts +0 -36
  74. /package/{skills/kook/SKILL.md → agent/skills/kook.md} +0 -0
package/src/ws.ts ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * KOOK WebSocket transport: kook-client wrapper and inbound message normalization.
3
+ */
4
+ import path from 'node:path';
5
+ import { Client } from 'kook-client';
6
+ import {
7
+ type KookInboundMessage,
8
+ type ResolvedKookConfig,
9
+ type ResolvedKookWebhookConfig,
10
+ type ResolvedKookWebsocketConfig,
11
+ } from './protocol.js';
12
+
13
+ /** Minimal client surface used by the endpoint (real kook-client or test mock). */
14
+ export interface KookClientTransport {
15
+ connect(): Promise<void>;
16
+ disconnect(): Promise<void>;
17
+ on(event: string, listener: (...args: unknown[]) => void): void;
18
+ removeAllListeners(): void;
19
+ sendChannelMsg(
20
+ channelId: string,
21
+ message: string,
22
+ ): Promise<{ msg_id?: string | number }>;
23
+ sendPrivateMsg(
24
+ userId: string,
25
+ message: string,
26
+ ): Promise<{ msg_id?: string | number }>;
27
+ recallMsg?(messageId: string): Promise<boolean>;
28
+ pickGuild(guildId: string): {
29
+ kick(userId: string): Promise<boolean>;
30
+ getRoleList(): Promise<Array<{
31
+ role_id: string | number;
32
+ name: string;
33
+ color?: number;
34
+ position?: number;
35
+ permissions?: unknown;
36
+ }>>;
37
+ createRole(name: string): Promise<{ role_id: string | number; name: string }>;
38
+ deleteRole(roleId: string): Promise<boolean>;
39
+ };
40
+ pickGuildMember(guildId: string, userId: string): {
41
+ addToBlackList(remark?: string, delMsgDays?: number): Promise<boolean>;
42
+ removeFromBlackList(): Promise<boolean>;
43
+ grant(roleId: string): Promise<boolean>;
44
+ revoke(roleId: string): Promise<boolean>;
45
+ setNickname(nickname: string): Promise<boolean>;
46
+ };
47
+ /** GET /api/v3/guild/list(kook-client 内部分页聚合)。 */
48
+ getGuildList(): Promise<Array<{ id: string | number; name?: string }>>;
49
+ /** GET /api/v3/channel/list(type: 1=文字 2=语音;is_category 为分组)。 */
50
+ getChannelList(guildId: string): Promise<Array<{
51
+ id: string | number;
52
+ name?: string;
53
+ type?: string | number;
54
+ is_category?: boolean;
55
+ }>>;
56
+ /** GET /api/v3/guild/user-list。 */
57
+ getGuildUserList(guildId: string, channelId?: string): Promise<unknown[]>;
58
+ self_id?: string | number;
59
+ }
60
+
61
+ export type CreateKookClient = (config: ResolvedKookConfig) => KookClientTransport;
62
+
63
+ export function normalizeKookMessage(raw: unknown): KookInboundMessage | null {
64
+ if (!raw || typeof raw !== 'object') return null;
65
+ const msg = raw as {
66
+ message_id?: string | number;
67
+ message_type?: string;
68
+ author_id?: string | number;
69
+ timestamp?: number;
70
+ raw_message?: string;
71
+ channel_id?: string | number;
72
+ author?: { info?: { nickname?: string; username?: string; bot?: boolean; roles?: number[] }; bot?: boolean; roles?: number[] };
73
+ message?: Array<{ type?: string; text?: string }>;
74
+ channel?: { info?: { guild_id?: string } };
75
+ };
76
+ if (msg.message_id == null || msg.author_id == null) return null;
77
+
78
+ const channelKind = msg.message_type === 'channel' ? 'channel' : 'private';
79
+ const channelId = channelKind === 'channel'
80
+ ? String(msg.channel_id ?? '')
81
+ : String(msg.author_id);
82
+ if (!channelId) return null;
83
+
84
+ const textParts: string[] = [];
85
+ for (const seg of msg.message ?? []) {
86
+ if (seg.type === 'text' || seg.type === 'markdown') {
87
+ if (seg.text) textParts.push(seg.text);
88
+ }
89
+ }
90
+ const content = textParts.join('') || msg.raw_message || '';
91
+
92
+ return {
93
+ id: String(msg.message_id),
94
+ content,
95
+ channelKind,
96
+ channelId,
97
+ authorId: String(msg.author_id),
98
+ authorName: msg.author?.info?.nickname
99
+ || msg.author?.info?.username
100
+ || String(msg.author_id),
101
+ authorBot: msg.author?.bot === true || msg.author?.info?.bot === true,
102
+ authorRoles: msg.author?.info?.roles ?? msg.author?.roles,
103
+ timestamp: msg.timestamp ?? Date.now(),
104
+ guildId: msg.channel?.info?.guild_id,
105
+ rawMessage: msg.raw_message,
106
+ };
107
+ }
108
+
109
+ export function defaultCreateClient(config: ResolvedKookWebsocketConfig): KookClientTransport {
110
+ return new Client({
111
+ token: config.token,
112
+ mode: 'websocket',
113
+ data_dir: config.data_dir || path.join(process.cwd(), 'data', 'kook'),
114
+ timeout: config.timeout,
115
+ max_retry: config.max_retry,
116
+ ignore: config.ignore,
117
+ logLevel: config.logLevel,
118
+ }) as unknown as KookClientTransport;
119
+ }
120
+
121
+ export function defaultCreateWebhookClient(config: ResolvedKookWebhookConfig): KookClientTransport {
122
+ return new Client({
123
+ token: config.token,
124
+ mode: 'webhook',
125
+ data_dir: path.join(process.cwd(), 'data', 'kook'),
126
+ timeout: 10_000,
127
+ max_retry: 3,
128
+ ignore: config.ignore,
129
+ logLevel: config.logLevel,
130
+ }) as unknown as KookClientTransport;
131
+ }
@@ -1,255 +0,0 @@
1
- import { useEffect, useState, useCallback } from 'react'
2
- import { apiFetch } from './utils/api'
3
- import { RefreshCw, Server, Wifi, WifiOff, Power, PowerOff, Loader2, Shield, Plus, Trash2, X } from 'lucide-react'
4
-
5
- interface BotInfo {
6
- name: string
7
- connected: boolean
8
- guildCount: number
9
- status: string
10
- }
11
-
12
- interface Role {
13
- id: string
14
- name: string
15
- color: number
16
- position: number
17
- }
18
-
19
- type Tab = 'overview' | 'roles'
20
-
21
- export default function KookDashboard() {
22
- const [bots, setBots] = useState<BotInfo[]>([])
23
- const [loading, setLoading] = useState(true)
24
- const [error, setError] = useState('')
25
- const [tab, setTab] = useState<Tab>('overview')
26
- const [actionLoading, setActionLoading] = useState<Record<string, boolean>>({})
27
-
28
- // Role management state
29
- const [selectedBot, setSelectedBot] = useState('')
30
- const [guildId, setGuildId] = useState('')
31
- const [roles, setRoles] = useState<Role[]>([])
32
- const [rolesLoading, setRolesLoading] = useState(false)
33
- const [newRoleName, setNewRoleName] = useState('')
34
- const [createLoading, setCreateLoading] = useState(false)
35
- const [deleteLoading, setDeleteLoading] = useState<string | null>(null)
36
-
37
- const fetchData = useCallback(async () => {
38
- setLoading(true)
39
- setError('')
40
- try {
41
- const res = await apiFetch('/api/kook/bots')
42
- const json = await res.json()
43
- if (json.success) setBots(json.data)
44
- else setError(json.error || '获取数据失败')
45
- } catch {
46
- setError('无法连接服务器')
47
- } finally {
48
- setLoading(false)
49
- }
50
- }, [])
51
-
52
- useEffect(() => { fetchData() }, [fetchData])
53
-
54
- const toggleConnect = async (name: string, connected: boolean) => {
55
- setActionLoading(prev => ({ ...prev, [name]: true }))
56
- try {
57
- const endpoint = connected ? 'disconnect' : 'connect'
58
- const res = await apiFetch(`/api/kook/bots/${encodeURIComponent(name)}/${endpoint}`, { method: 'POST' })
59
- const json = await res.json()
60
- if (!json.success) setError(json.error || '操作失败')
61
- await fetchData()
62
- } catch {
63
- setError('操作失败')
64
- } finally {
65
- setActionLoading(prev => ({ ...prev, [name]: false }))
66
- }
67
- }
68
-
69
- const loadRoles = async () => {
70
- if (!selectedBot || !guildId) return
71
- setRolesLoading(true)
72
- setRoles([])
73
- try {
74
- const res = await apiFetch(`/api/kook/bots/${encodeURIComponent(selectedBot)}/guilds/${encodeURIComponent(guildId)}/roles`)
75
- const json = await res.json()
76
- if (json.success) setRoles(json.data)
77
- else setError(json.error || '获取角色失败')
78
- } catch {
79
- setError('获取角色失败')
80
- } finally {
81
- setRolesLoading(false)
82
- }
83
- }
84
-
85
- const createRole = async () => {
86
- if (!selectedBot || !guildId || !newRoleName.trim()) return
87
- setCreateLoading(true)
88
- try {
89
- const res = await apiFetch(`/api/kook/bots/${encodeURIComponent(selectedBot)}/guilds/${encodeURIComponent(guildId)}/roles`, {
90
- method: 'POST',
91
- headers: { 'Content-Type': 'application/json' },
92
- body: JSON.stringify({ name: newRoleName.trim() }),
93
- })
94
- const json = await res.json()
95
- if (json.success) {
96
- setNewRoleName('')
97
- await loadRoles()
98
- } else {
99
- setError(json.error || '创建角色失败')
100
- }
101
- } catch {
102
- setError('创建角色失败')
103
- } finally {
104
- setCreateLoading(false)
105
- }
106
- }
107
-
108
- const deleteRole = async (roleId: string) => {
109
- if (!selectedBot || !guildId) return
110
- setDeleteLoading(roleId)
111
- try {
112
- const res = await apiFetch(`/api/kook/bots/${encodeURIComponent(selectedBot)}/guilds/${encodeURIComponent(guildId)}/roles/${encodeURIComponent(roleId)}`, {
113
- method: 'DELETE',
114
- })
115
- const json = await res.json()
116
- if (json.success) {
117
- setRoles(prev => prev.filter(r => r.id !== roleId))
118
- } else {
119
- setError(json.error || '删除角色失败')
120
- }
121
- } catch {
122
- setError('删除角色失败')
123
- } finally {
124
- setDeleteLoading(null)
125
- }
126
- }
127
-
128
- const onlineBots = bots.filter(b => b.connected)
129
-
130
- return (
131
- <div className="p-6 max-w-5xl mx-auto">
132
- <div className="flex items-center justify-between mb-6">
133
- <h1 className="text-2xl font-bold flex items-center gap-2">
134
- <Server className="w-6 h-6" /> KOOK 机器人
135
- </h1>
136
- <button onClick={fetchData} disabled={loading}
137
- className="flex items-center gap-1 px-3 py-1.5 rounded bg-sky-500 text-white hover:bg-sky-600 disabled:opacity-50 text-sm">
138
- <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} /> 刷新
139
- </button>
140
- </div>
141
-
142
- {error && <div className="mb-4 p-3 bg-red-50 text-red-600 rounded border border-red-200 flex items-center justify-between">
143
- <span>{error}</span>
144
- <button onClick={() => setError('')} className="text-red-400 hover:text-red-600"><X className="w-4 h-4" /></button>
145
- </div>}
146
-
147
- {/* Tabs */}
148
- <div className="flex gap-1 mb-6 border-b">
149
- <button onClick={() => setTab('overview')}
150
- className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === 'overview' ? 'border-sky-500 text-sky-600' : 'border-transparent text-gray-500 hover:text-gray-700'}`}>
151
- 概览
152
- </button>
153
- <button onClick={() => setTab('roles')}
154
- className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === 'roles' ? 'border-sky-500 text-sky-600' : 'border-transparent text-gray-500 hover:text-gray-700'}`}>
155
- 角色管理
156
- </button>
157
- </div>
158
-
159
- {/* Overview Tab */}
160
- {tab === 'overview' && (
161
- <>
162
- {!loading && !bots.length && !error && (
163
- <div className="text-center text-gray-500 py-12">暂无 KOOK 机器人实例</div>
164
- )}
165
- <div className="grid gap-4 md:grid-cols-2">
166
- {bots.map((bot) => (
167
- <div key={bot.name} className="border rounded-lg p-4 bg-card shadow-sm">
168
- <div className="flex items-center justify-between mb-3">
169
- <span className="font-medium text-lg">{bot.name}</span>
170
- {bot.connected
171
- ? <span className="flex items-center gap-1 text-green-600 text-sm"><Wifi className="w-4 h-4" /> 在线</span>
172
- : <span className="flex items-center gap-1 text-gray-400 text-sm"><WifiOff className="w-4 h-4" /> 离线</span>}
173
- </div>
174
- <div className="text-sm text-gray-600 mb-3">
175
- <div className="flex justify-between"><span>服务器</span><span className="font-mono">{bot.guildCount}</span></div>
176
- </div>
177
- <button
178
- onClick={() => toggleConnect(bot.name, bot.connected)}
179
- disabled={actionLoading[bot.name]}
180
- className={`flex items-center gap-1 px-3 py-1.5 rounded text-sm text-white ${bot.connected ? 'bg-red-500 hover:bg-red-600' : 'bg-green-500 hover:bg-green-600'} disabled:opacity-50`}>
181
- {actionLoading[bot.name]
182
- ? <Loader2 className="w-3.5 h-3.5 animate-spin" />
183
- : bot.connected ? <PowerOff className="w-3.5 h-3.5" /> : <Power className="w-3.5 h-3.5" />}
184
- {bot.connected ? '断开' : '连接'}
185
- </button>
186
- </div>
187
- ))}
188
- </div>
189
- </>
190
- )}
191
-
192
- {/* Roles Tab */}
193
- {tab === 'roles' && (
194
- <div className="space-y-4">
195
- {/* Bot + Guild selector */}
196
- <div className="flex flex-wrap items-end gap-3">
197
- <div>
198
- <label className="block text-xs text-gray-500 mb-1">机器人</label>
199
- <select value={selectedBot} onChange={(e) => setSelectedBot(e.target.value)}
200
- className="border rounded px-2 py-1.5 text-sm min-w-[140px]">
201
- <option value="">--</option>
202
- {onlineBots.map(b => <option key={b.name} value={b.name}>{b.name}</option>)}
203
- </select>
204
- </div>
205
- <div>
206
- <label className="block text-xs text-gray-500 mb-1">服务器 ID</label>
207
- <input value={guildId} onChange={(e) => setGuildId(e.target.value)} placeholder="输入服务器 ID"
208
- className="border rounded px-2 py-1.5 text-sm w-[180px]" />
209
- </div>
210
- <button onClick={loadRoles} disabled={!selectedBot || !guildId || rolesLoading}
211
- className="px-3 py-1.5 rounded bg-sky-500 text-white text-sm hover:bg-sky-600 disabled:opacity-50 flex items-center gap-1">
212
- {rolesLoading ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Shield className="w-3.5 h-3.5" />} 查询角色
213
- </button>
214
- </div>
215
-
216
- {!onlineBots.length && <div className="text-center text-gray-500 py-4">暂无在线机器人</div>}
217
-
218
- {/* Create Role */}
219
- {selectedBot && guildId && roles.length > 0 && (
220
- <div className="flex items-center gap-2">
221
- <input value={newRoleName} onChange={(e) => setNewRoleName(e.target.value)} placeholder="新角色名称"
222
- className="border rounded px-2 py-1.5 text-sm flex-1 max-w-[240px]"
223
- onKeyDown={(e) => e.key === 'Enter' && createRole()} />
224
- <button onClick={createRole} disabled={createLoading || !newRoleName.trim()}
225
- className="px-3 py-1.5 rounded bg-green-500 text-white text-sm hover:bg-green-600 disabled:opacity-50 flex items-center gap-1">
226
- {createLoading ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Plus className="w-3.5 h-3.5" />} 创建
227
- </button>
228
- </div>
229
- )}
230
-
231
- {/* Roles list */}
232
- {rolesLoading && <div className="flex justify-center py-8"><Loader2 className="w-6 h-6 animate-spin text-sky-500" /></div>}
233
-
234
- {!rolesLoading && roles.length > 0 && (
235
- <div className="border rounded-lg bg-card shadow-sm divide-y">
236
- {roles.map(role => (
237
- <div key={role.id} className="flex items-center px-4 py-2.5 hover:bg-gray-50">
238
- <div className="w-3 h-3 rounded-full mr-3" style={{ backgroundColor: role.color ? `#${role.color.toString(16).padStart(6, '0')}` : '#999' }} />
239
- <span className="font-medium text-sm flex-1">{role.name}</span>
240
- <span className="text-xs text-gray-400 mr-4">ID: {role.id}</span>
241
- <button onClick={() => deleteRole(role.id)} disabled={deleteLoading === role.id}
242
- className="text-gray-400 hover:text-red-500 disabled:opacity-50">
243
- {deleteLoading === role.id ? <Loader2 className="w-4 h-4 animate-spin" /> : <Trash2 className="w-4 h-4" />}
244
- </button>
245
- </div>
246
- ))}
247
- </div>
248
- )}
249
-
250
- {!rolesLoading && selectedBot && guildId && !roles.length && <div className="text-center text-gray-400 py-6">暂无角色数据,请先查询</div>}
251
- </div>
252
- )}
253
- </div>
254
- )
255
- }
package/client/index.tsx DELETED
@@ -1,11 +0,0 @@
1
- import type { PluginRegisterHostApi } from '@zhin.js/console-types'
2
- import KookDashboard from './Dashboard'
3
-
4
- export function register(api: PluginRegisterHostApi) {
5
- api.addRoute({
6
- path: '/console/kook',
7
- name: 'KOOK',
8
- element: api.React.createElement(KookDashboard, { hostReact: api.React }),
9
- })
10
- api.addTool({ id: 'kook', name: 'KOOK', path: '/console/kook' })
11
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "../node_modules/@zhin.js/console/browser.tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../dist"
5
- },
6
- "include": ["./**/*"]
7
- }
@@ -1,17 +0,0 @@
1
- const TOKEN_KEY = "zhin_api_token";
2
-
3
- export function getToken(): string | null {
4
- return localStorage.getItem(TOKEN_KEY);
5
- }
6
-
7
- export async function apiFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
8
- const token = getToken();
9
- const headers = new Headers(init?.headers);
10
- if (token) headers.set("Authorization", `Bearer ${token}`);
11
- const res = await fetch(input, { ...init, headers });
12
- if (res.status === 401) {
13
- localStorage.removeItem(TOKEN_KEY);
14
- window.dispatchEvent(new CustomEvent("zhin:auth-required"));
15
- }
16
- return res;
17
- }
package/dist/index.js DELETED
@@ -1,31 +0,0 @@
1
- import{useEffect as Be,useState as p,useCallback as Me}from"react";var Y="zhin_api_token";function se(){return localStorage.getItem(Y)}async function S(t,u){let f=se(),s=new Headers(u?.headers);f&&s.set("Authorization",`Bearer ${f}`);let m=await fetch(t,{...u,headers:s});return m.status===401&&(localStorage.removeItem(Y),window.dispatchEvent(new CustomEvent("zhin:auth-required"))),m}import{forwardRef as pe,createElement as me}from"react";var O=(...t)=>t.filter((u,f,s)=>!!u&&u.trim()!==""&&s.indexOf(u)===f).join(" ").trim();var ee=t=>t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();var ae=t=>t.replace(/^([A-Z])|[\s-_]+(\w)/g,(u,f,s)=>s?s.toUpperCase():f.toLowerCase());var V=t=>{let u=ae(t);return u.charAt(0).toUpperCase()+u.slice(1)};import{forwardRef as ne,createElement as ue}from"react";var H={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};var te=t=>{for(let u in t)if(u.startsWith("aria-")||u==="role"||u==="title")return!0;return!1};import{createContext as re,useContext as ie,useMemo as He,createElement as Ge}from"react";var ce=re({});var oe=()=>ie(ce);var de=ne(({color:t,size:u,strokeWidth:f,absoluteStrokeWidth:s,className:m="",children:l,iconNode:x,...g},q)=>{let{size:C=24,strokeWidth:r=2,absoluteStrokeWidth:G=!1,color:c="currentColor",className:W=""}=oe()??{},h=s??G?Number(f??r)*24/Number(u??C):f??r;return ue("svg",{ref:q,...H,width:u??C??H.width,height:u??C??H.height,stroke:t??c,strokeWidth:h,className:O("lucide",W,m),...!l&&!te(g)&&{"aria-hidden":"true"},...g},[...x.map(([k,I])=>ue(k,I)),...Array.isArray(l)?l:[l]])});var d=(t,u)=>{let f=pe(({className:s,...m},l)=>me(de,{ref:l,iconNode:u,className:O(`lucide-${ee(V(t))}`,`lucide-${t}`,s),...m}));return f.displayName=V(t),f};var Le=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],n=d("loader-circle",Le);var Ie=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],w=d("plus",Ie);var xe=[["path",{d:"M18.36 6.64A9 9 0 0 1 20.77 15",key:"dxknvb"}],["path",{d:"M6.16 6.16a9 9 0 1 0 12.68 12.68",key:"1x7qb5"}],["path",{d:"M12 2v4",key:"3427ic"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],P=d("power-off",xe);var Ce=[["path",{d:"M12 2v10",key:"mnfbl"}],["path",{d:"M18.4 6.6a9 9 0 1 1-12.77.04",key:"obofu9"}]],A=d("power",Ce);var he=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],B=d("refresh-cw",he);var Se=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],M=d("server",Se);var ge=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]],F=d("shield",ge);var ke=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],D=d("trash-2",ke);var we=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],y=d("wifi-off",we);var Pe=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],R=d("wifi",Pe);var Ae=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],T=d("x",Ae);import{Fragment as Fe,jsx as a,jsxs as o}from"react/jsx-runtime";function E(){let[t,u]=p([]),[f,s]=p(!0),[m,l]=p(""),[x,g]=p("overview"),[q,C]=p({}),[r,G]=p(""),[c,W]=p(""),[h,k]=p([]),[I,N]=p(!1),[b,z]=p(""),[X,K]=p(!1),[Z,Q]=p(null),v=Me(async()=>{s(!0),l("");try{let i=await(await S("/api/kook/bots")).json();i.success?u(i.data):l(i.error||"\u83B7\u53D6\u6570\u636E\u5931\u8D25")}catch{l("\u65E0\u6CD5\u8FDE\u63A5\u670D\u52A1\u5668")}finally{s(!1)}},[]);Be(()=>{v()},[v]);let le=async(e,i)=>{C(L=>({...L,[e]:!0}));try{let L=i?"disconnect":"connect",U=await(await S(`/api/kook/bots/${encodeURIComponent(e)}/${L}`,{method:"POST"})).json();U.success||l(U.error||"\u64CD\u4F5C\u5931\u8D25"),await v()}catch{l("\u64CD\u4F5C\u5931\u8D25")}finally{C(L=>({...L,[e]:!1}))}},J=async()=>{if(!(!r||!c)){N(!0),k([]);try{let i=await(await S(`/api/kook/bots/${encodeURIComponent(r)}/guilds/${encodeURIComponent(c)}/roles`)).json();i.success?k(i.data):l(i.error||"\u83B7\u53D6\u89D2\u8272\u5931\u8D25")}catch{l("\u83B7\u53D6\u89D2\u8272\u5931\u8D25")}finally{N(!1)}}},j=async()=>{if(!(!r||!c||!b.trim())){K(!0);try{let i=await(await S(`/api/kook/bots/${encodeURIComponent(r)}/guilds/${encodeURIComponent(c)}/roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:b.trim()})})).json();i.success?(z(""),await J()):l(i.error||"\u521B\u5EFA\u89D2\u8272\u5931\u8D25")}catch{l("\u521B\u5EFA\u89D2\u8272\u5931\u8D25")}finally{K(!1)}}},fe=async e=>{if(!(!r||!c)){Q(e);try{let L=await(await S(`/api/kook/bots/${encodeURIComponent(r)}/guilds/${encodeURIComponent(c)}/roles/${encodeURIComponent(e)}`,{method:"DELETE"})).json();L.success?k($=>$.filter(U=>U.id!==e)):l(L.error||"\u5220\u9664\u89D2\u8272\u5931\u8D25")}catch{l("\u5220\u9664\u89D2\u8272\u5931\u8D25")}finally{Q(null)}}},_=t.filter(e=>e.connected);return o("div",{className:"p-6 max-w-5xl mx-auto",children:[o("div",{className:"flex items-center justify-between mb-6",children:[o("h1",{className:"text-2xl font-bold flex items-center gap-2",children:[a(M,{className:"w-6 h-6"})," KOOK \u673A\u5668\u4EBA"]}),o("button",{onClick:v,disabled:f,className:"flex items-center gap-1 px-3 py-1.5 rounded bg-sky-500 text-white hover:bg-sky-600 disabled:opacity-50 text-sm",children:[a(B,{className:`w-4 h-4 ${f?"animate-spin":""}`})," \u5237\u65B0"]})]}),m&&o("div",{className:"mb-4 p-3 bg-red-50 text-red-600 rounded border border-red-200 flex items-center justify-between",children:[a("span",{children:m}),a("button",{onClick:()=>l(""),className:"text-red-400 hover:text-red-600",children:a(T,{className:"w-4 h-4"})})]}),o("div",{className:"flex gap-1 mb-6 border-b",children:[a("button",{onClick:()=>g("overview"),className:`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${x==="overview"?"border-sky-500 text-sky-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:"\u6982\u89C8"}),a("button",{onClick:()=>g("roles"),className:`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${x==="roles"?"border-sky-500 text-sky-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:"\u89D2\u8272\u7BA1\u7406"})]}),x==="overview"&&o(Fe,{children:[!f&&!t.length&&!m&&a("div",{className:"text-center text-gray-500 py-12",children:"\u6682\u65E0 KOOK \u673A\u5668\u4EBA\u5B9E\u4F8B"}),a("div",{className:"grid gap-4 md:grid-cols-2",children:t.map(e=>o("div",{className:"border rounded-lg p-4 bg-card shadow-sm",children:[o("div",{className:"flex items-center justify-between mb-3",children:[a("span",{className:"font-medium text-lg",children:e.name}),e.connected?o("span",{className:"flex items-center gap-1 text-green-600 text-sm",children:[a(R,{className:"w-4 h-4"})," \u5728\u7EBF"]}):o("span",{className:"flex items-center gap-1 text-gray-400 text-sm",children:[a(y,{className:"w-4 h-4"})," \u79BB\u7EBF"]})]}),a("div",{className:"text-sm text-gray-600 mb-3",children:o("div",{className:"flex justify-between",children:[a("span",{children:"\u670D\u52A1\u5668"}),a("span",{className:"font-mono",children:e.guildCount})]})}),o("button",{onClick:()=>le(e.name,e.connected),disabled:q[e.name],className:`flex items-center gap-1 px-3 py-1.5 rounded text-sm text-white ${e.connected?"bg-red-500 hover:bg-red-600":"bg-green-500 hover:bg-green-600"} disabled:opacity-50`,children:[q[e.name]?a(n,{className:"w-3.5 h-3.5 animate-spin"}):e.connected?a(P,{className:"w-3.5 h-3.5"}):a(A,{className:"w-3.5 h-3.5"}),e.connected?"\u65AD\u5F00":"\u8FDE\u63A5"]})]},e.name))})]}),x==="roles"&&o("div",{className:"space-y-4",children:[o("div",{className:"flex flex-wrap items-end gap-3",children:[o("div",{children:[a("label",{className:"block text-xs text-gray-500 mb-1",children:"\u673A\u5668\u4EBA"}),o("select",{value:r,onChange:e=>G(e.target.value),className:"border rounded px-2 py-1.5 text-sm min-w-[140px]",children:[a("option",{value:"",children:"--"}),_.map(e=>a("option",{value:e.name,children:e.name},e.name))]})]}),o("div",{children:[a("label",{className:"block text-xs text-gray-500 mb-1",children:"\u670D\u52A1\u5668 ID"}),a("input",{value:c,onChange:e=>W(e.target.value),placeholder:"\u8F93\u5165\u670D\u52A1\u5668 ID",className:"border rounded px-2 py-1.5 text-sm w-[180px]"})]}),o("button",{onClick:J,disabled:!r||!c||I,className:"px-3 py-1.5 rounded bg-sky-500 text-white text-sm hover:bg-sky-600 disabled:opacity-50 flex items-center gap-1",children:[I?a(n,{className:"w-3.5 h-3.5 animate-spin"}):a(F,{className:"w-3.5 h-3.5"})," \u67E5\u8BE2\u89D2\u8272"]})]}),!_.length&&a("div",{className:"text-center text-gray-500 py-4",children:"\u6682\u65E0\u5728\u7EBF\u673A\u5668\u4EBA"}),r&&c&&h.length>0&&o("div",{className:"flex items-center gap-2",children:[a("input",{value:b,onChange:e=>z(e.target.value),placeholder:"\u65B0\u89D2\u8272\u540D\u79F0",className:"border rounded px-2 py-1.5 text-sm flex-1 max-w-[240px]",onKeyDown:e=>e.key==="Enter"&&j()}),o("button",{onClick:j,disabled:X||!b.trim(),className:"px-3 py-1.5 rounded bg-green-500 text-white text-sm hover:bg-green-600 disabled:opacity-50 flex items-center gap-1",children:[X?a(n,{className:"w-3.5 h-3.5 animate-spin"}):a(w,{className:"w-3.5 h-3.5"})," \u521B\u5EFA"]})]}),I&&a("div",{className:"flex justify-center py-8",children:a(n,{className:"w-6 h-6 animate-spin text-sky-500"})}),!I&&h.length>0&&a("div",{className:"border rounded-lg bg-card shadow-sm divide-y",children:h.map(e=>o("div",{className:"flex items-center px-4 py-2.5 hover:bg-gray-50",children:[a("div",{className:"w-3 h-3 rounded-full mr-3",style:{backgroundColor:e.color?`#${e.color.toString(16).padStart(6,"0")}`:"#999"}}),a("span",{className:"font-medium text-sm flex-1",children:e.name}),o("span",{className:"text-xs text-gray-400 mr-4",children:["ID: ",e.id]}),a("button",{onClick:()=>fe(e.id),disabled:Z===e.id,className:"text-gray-400 hover:text-red-500 disabled:opacity-50",children:Z===e.id?a(n,{className:"w-4 h-4 animate-spin"}):a(D,{className:"w-4 h-4"})})]},e.id))}),!I&&r&&c&&!h.length&&a("div",{className:"text-center text-gray-400 py-6",children:"\u6682\u65E0\u89D2\u8272\u6570\u636E\uFF0C\u8BF7\u5148\u67E5\u8BE2"})]})]})}function Ga(t){t.addRoute({path:"/console/kook",name:"KOOK",element:t.React.createElement(E,{hostReact:t.React})}),t.addTool({id:"kook",name:"KOOK",path:"/console/kook"})}export{Ga as register};
2
- /*! Bundled license information:
3
-
4
- lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs:
5
- lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs:
6
- lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs:
7
- lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs:
8
- lucide-react/dist/esm/defaultAttributes.mjs:
9
- lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs:
10
- lucide-react/dist/esm/context.mjs:
11
- lucide-react/dist/esm/Icon.mjs:
12
- lucide-react/dist/esm/createLucideIcon.mjs:
13
- lucide-react/dist/esm/icons/loader-circle.mjs:
14
- lucide-react/dist/esm/icons/plus.mjs:
15
- lucide-react/dist/esm/icons/power-off.mjs:
16
- lucide-react/dist/esm/icons/power.mjs:
17
- lucide-react/dist/esm/icons/refresh-cw.mjs:
18
- lucide-react/dist/esm/icons/server.mjs:
19
- lucide-react/dist/esm/icons/shield.mjs:
20
- lucide-react/dist/esm/icons/trash-2.mjs:
21
- lucide-react/dist/esm/icons/wifi-off.mjs:
22
- lucide-react/dist/esm/icons/wifi.mjs:
23
- lucide-react/dist/esm/icons/x.mjs:
24
- lucide-react/dist/esm/lucide-react.mjs:
25
- (**
26
- * @license lucide-react v1.16.0 - ISC
27
- *
28
- * This source code is licensed under the ISC license.
29
- * See the LICENSE file in the root directory of this source tree.
30
- *)
31
- */
package/lib/adapter.d.ts DELETED
@@ -1,26 +0,0 @@
1
- /**
2
- * KOOK 适配器
3
- */
4
- import { Adapter, Plugin } from "zhin.js";
5
- import { KookBot } from "./bot.js";
6
- import type { KookBotConfig } from "./types.js";
7
- export declare class KookAdapter extends Adapter<KookBot> {
8
- constructor(plugin: Plugin);
9
- createBot(config: KookBotConfig): KookBot;
10
- kickMember(botId: string, sceneId: string, userId: string): Promise<boolean>;
11
- banMember(botId: string, sceneId: string, userId: string, reason?: string): Promise<boolean>;
12
- unbanMember(botId: string, sceneId: string, userId: string): Promise<boolean>;
13
- setMemberNickname(botId: string, sceneId: string, userId: string, nickname: string): Promise<boolean>;
14
- listMembers(botId: string, sceneId: string): Promise<{
15
- members: {
16
- id: string;
17
- username: string;
18
- nickname: string;
19
- roles: number[];
20
- }[];
21
- count: number;
22
- }>;
23
- start(): Promise<void>;
24
- stop(): Promise<void>;
25
- }
26
- //# sourceMappingURL=adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,OAAO,EACP,MAAM,EACP,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,WAAY,SAAQ,OAAO,CAAC,OAAO,CAAC;gBACnC,MAAM,EAAE,MAAM;IAI1B,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO;IAMnC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAMzD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAMzE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAM1D,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAMlF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;IAe1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B"}
package/lib/adapter.js DELETED
@@ -1,61 +0,0 @@
1
- /**
2
- * KOOK 适配器
3
- */
4
- import { Adapter, } from "zhin.js";
5
- import { KookBot } from "./bot.js";
6
- export class KookAdapter extends Adapter {
7
- constructor(plugin) {
8
- super(plugin, "kook", []);
9
- }
10
- createBot(config) {
11
- return new KookBot(this, config);
12
- }
13
- // ── IGroupManagement 标准群管方法 ──────────────────────────────────
14
- async kickMember(botId, sceneId, userId) {
15
- const bot = this.bots.get(botId);
16
- if (!bot)
17
- throw new Error(`Bot ${botId} 不存在`);
18
- return bot.kickUser(sceneId, userId);
19
- }
20
- async banMember(botId, sceneId, userId, reason) {
21
- const bot = this.bots.get(botId);
22
- if (!bot)
23
- throw new Error(`Bot ${botId} 不存在`);
24
- return bot.addToBlacklist(sceneId, userId, reason);
25
- }
26
- async unbanMember(botId, sceneId, userId) {
27
- const bot = this.bots.get(botId);
28
- if (!bot)
29
- throw new Error(`Bot ${botId} 不存在`);
30
- return bot.removeFromBlacklist(sceneId, userId);
31
- }
32
- async setMemberNickname(botId, sceneId, userId, nickname) {
33
- const bot = this.bots.get(botId);
34
- if (!bot)
35
- throw new Error(`Bot ${botId} 不存在`);
36
- return bot.setNickname(sceneId, userId, nickname);
37
- }
38
- async listMembers(botId, sceneId) {
39
- const bot = this.bots.get(botId);
40
- if (!bot)
41
- throw new Error(`Bot ${botId} 不存在`);
42
- const members = await bot.getGuildMembers(sceneId);
43
- return {
44
- members: members.map(m => ({
45
- id: m.id, username: m.username,
46
- nickname: m.nickname, roles: m.roles,
47
- })),
48
- count: members.length,
49
- };
50
- }
51
- // ── 生命周期 ───────────────────────────────────────────────────────
52
- async start() {
53
- await super.start();
54
- this.plugin.logger.info("KOOK 适配器已启动");
55
- }
56
- async stop() {
57
- await super.stop();
58
- this.plugin.logger.info("KOOK 适配器已停止");
59
- }
60
- }
61
- //# sourceMappingURL=adapter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}