@zhin.js/adapter-icqq 1.0.71 → 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.
Files changed (122) hide show
  1. package/CHANGELOG.md +1216 -52
  2. package/PERMITS.md +24 -0
  3. package/README.md +123 -429
  4. package/adapters/icqq/index.js +31 -0
  5. package/adapters/icqq/index.ts +42 -0
  6. package/agents/icqq/agent.json +20 -0
  7. package/agents/icqq/boundaries.md +3 -0
  8. package/agents/icqq/conventions.md +3 -0
  9. package/agents/icqq/skills/icqq-directory/SKILL.md +20 -0
  10. package/agents/icqq/skills/icqq-directory/tools/friend_list/index.js +19 -0
  11. package/agents/icqq/skills/icqq-directory/tools/friend_list/index.ts +19 -0
  12. package/agents/icqq/skills/icqq-directory/tools/group_list/index.js +20 -0
  13. package/agents/icqq/skills/icqq-directory/tools/group_list/index.ts +20 -0
  14. package/agents/icqq/skills/icqq-directory/tools/group_member_info/index.js +26 -0
  15. package/agents/icqq/skills/icqq-directory/tools/group_member_info/index.ts +28 -0
  16. package/agents/icqq/skills/icqq-directory/tools/user_info/index.js +15 -0
  17. package/agents/icqq/skills/icqq-directory/tools/user_info/index.ts +14 -0
  18. package/agents/icqq/skills/icqq-group-admin/SKILL.md +25 -0
  19. package/agents/icqq/skills/icqq-group-admin/tools/announce/index.js +18 -0
  20. package/agents/icqq/skills/icqq-group-admin/tools/announce/index.ts +17 -0
  21. package/agents/icqq/skills/icqq-group-admin/tools/essence/index.js +24 -0
  22. package/agents/icqq/skills/icqq-group-admin/tools/essence/index.ts +22 -0
  23. package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.js +23 -0
  24. package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.ts +20 -0
  25. package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.js +27 -0
  26. package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.ts +23 -0
  27. package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.js +23 -0
  28. package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.ts +21 -0
  29. package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js +25 -0
  30. package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts +22 -0
  31. package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.js +26 -0
  32. package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts +23 -0
  33. package/agents/icqq/skills/icqq-group-state/SKILL.md +17 -0
  34. package/agents/icqq/skills/icqq-group-state/tools/group_files/index.js +24 -0
  35. package/agents/icqq/skills/icqq-group-state/tools/group_files/index.ts +22 -0
  36. package/agents/icqq/skills/icqq-group-state/tools/list_muted/index.js +18 -0
  37. package/agents/icqq/skills/icqq-group-state/tools/list_muted/index.ts +17 -0
  38. package/agents/icqq/skills/icqq-interaction/SKILL.md +19 -0
  39. package/agents/icqq/skills/icqq-interaction/tools/group_sign/index.js +20 -0
  40. package/agents/icqq/skills/icqq-interaction/tools/group_sign/index.ts +18 -0
  41. package/agents/icqq/skills/icqq-interaction/tools/poke/index.js +17 -0
  42. package/agents/icqq/skills/icqq-interaction/tools/poke/index.ts +16 -0
  43. package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.js +26 -0
  44. package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.ts +22 -0
  45. package/agents/icqq/system.md +3 -0
  46. package/commands/icqq/endpoint/add/[[id]]/index.js +3 -0
  47. package/commands/icqq/endpoint/add/[[id]]/index.ts +3 -0
  48. package/commands/icqq/endpoint/definition.js +31 -0
  49. package/commands/icqq/endpoint/definition.ts +32 -0
  50. package/commands/icqq/endpoint/list/index.js +3 -0
  51. package/commands/icqq/endpoint/list/index.ts +3 -0
  52. package/commands/icqq/endpoint/remove/[id]/index.js +3 -0
  53. package/commands/icqq/endpoint/remove/[id]/index.ts +3 -0
  54. package/lib/client.d.ts +19 -0
  55. package/lib/client.js +3 -0
  56. package/lib/content-resolver.d.ts +10 -0
  57. package/lib/content-resolver.js +98 -0
  58. package/lib/cq-message.d.ts +14 -0
  59. package/lib/cq-message.js +106 -0
  60. package/lib/endpoint.d.ts +30 -0
  61. package/lib/endpoint.js +1003 -0
  62. package/lib/forward-msg.d.ts +6 -0
  63. package/lib/forward-msg.js +107 -0
  64. package/lib/icqq-guild.d.ts +57 -0
  65. package/lib/icqq-guild.js +109 -0
  66. package/lib/icqq-inbound.d.ts +165 -0
  67. package/lib/icqq-inbound.js +651 -0
  68. package/lib/icqq-inbox.d.ts +25 -0
  69. package/lib/icqq-inbox.js +129 -0
  70. package/lib/icqq-login-assist.d.ts +27 -0
  71. package/lib/icqq-login-assist.js +109 -0
  72. package/lib/icqq-runtime-state.d.ts +1 -0
  73. package/lib/icqq-runtime-state.js +6 -0
  74. package/lib/index.d.ts +5 -18
  75. package/lib/index.js +5 -124
  76. package/lib/outbound-media.d.ts +14 -0
  77. package/lib/outbound-media.js +76 -0
  78. package/lib/protocol.d.ts +258 -0
  79. package/lib/protocol.js +422 -0
  80. package/lib/types.d.ts +33 -13
  81. package/lib/types.js +0 -1
  82. package/package.json +86 -32
  83. package/plugin.js +19 -0
  84. package/prompt-sections/platform/index.js +16 -0
  85. package/prompt-sections/platform/index.ts +16 -0
  86. package/schema.json +168 -0
  87. package/src/client.ts +29 -0
  88. package/src/content-resolver.ts +131 -0
  89. package/src/cq-message.ts +113 -0
  90. package/src/endpoint.ts +1084 -0
  91. package/src/forward-msg.ts +112 -0
  92. package/src/icqq-guild.ts +154 -0
  93. package/src/icqq-inbound.ts +814 -0
  94. package/src/icqq-inbox.ts +147 -0
  95. package/src/icqq-login-assist.ts +132 -0
  96. package/src/icqq-runtime-state.ts +7 -0
  97. package/src/index.ts +30 -149
  98. package/src/outbound-media.ts +103 -0
  99. package/src/protocol.ts +551 -0
  100. package/src/types.ts +35 -13
  101. package/LICENSE +0 -21
  102. package/client/ICQQManagement.tsx +0 -139
  103. package/client/LoginAssistPanel.tsx +0 -293
  104. package/client/index.tsx +0 -11
  105. package/client/tsconfig.json +0 -7
  106. package/client/utils/api.ts +0 -20
  107. package/dist/index.js +0 -1
  108. package/lib/adapter.d.ts +0 -34
  109. package/lib/adapter.d.ts.map +0 -1
  110. package/lib/adapter.js +0 -396
  111. package/lib/adapter.js.map +0 -1
  112. package/lib/bot.d.ts +0 -46
  113. package/lib/bot.d.ts.map +0 -1
  114. package/lib/bot.js +0 -457
  115. package/lib/bot.js.map +0 -1
  116. package/lib/index.d.ts.map +0 -1
  117. package/lib/index.js.map +0 -1
  118. package/lib/types.d.ts.map +0 -1
  119. package/lib/types.js.map +0 -1
  120. package/skills/icqq/SKILL.md +0 -22
  121. package/src/adapter.ts +0 -448
  122. package/src/bot.ts +0 -521
@@ -1,139 +0,0 @@
1
- import { useCallback, useEffect, useState, type CSSProperties } from "react";
2
- import { Activity, Bot, LogIn, Users } from "lucide-react";
3
- import LoginAssistPanel from "./LoginAssistPanel";
4
- import { apiFetch } from "./utils/api";
5
-
6
- const shell: CSSProperties = { display: "flex", flexDirection: "column", gap: "1.25rem" };
7
- const title: CSSProperties = { fontSize: "1.5rem", fontWeight: 700, margin: 0 };
8
- const sub: CSSProperties = { color: "hsl(var(--muted-foreground))", fontSize: "0.875rem", margin: "0.25rem 0 0" };
9
- const tabsRow: CSSProperties = { display: "flex", gap: "0.25rem", borderBottom: "1px solid hsl(var(--border))", paddingBottom: 2 };
10
- const tabBtn = (active: boolean): CSSProperties => ({
11
- padding: "0.5rem 1rem",
12
- fontSize: "0.875rem",
13
- fontWeight: 500,
14
- border: "none",
15
- borderRadius: "calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0 0",
16
- cursor: "pointer",
17
- background: active ? "hsl(var(--muted))" : "transparent",
18
- color: active ? "hsl(var(--foreground))" : "hsl(var(--muted-foreground))",
19
- });
20
- const card: CSSProperties = {
21
- background: "hsl(var(--card))",
22
- border: "1px solid hsl(var(--border))",
23
- borderRadius: "var(--radius)",
24
- padding: "1rem",
25
- };
26
-
27
- interface BotRow {
28
- name: string;
29
- connected: boolean;
30
- groupCount: number;
31
- friendCount: number;
32
- loginMode: string;
33
- status: string;
34
- }
35
-
36
- export default function ICQQManagement() {
37
- const [tab, setTab] = useState<"overview" | "login">("overview");
38
- const [bots, setBots] = useState<BotRow[]>([]);
39
- const [loading, setLoading] = useState(true);
40
- const [err, setErr] = useState<string | null>(null);
41
-
42
- const loadBots = useCallback(async () => {
43
- setLoading(true);
44
- setErr(null);
45
- try {
46
- const res = await apiFetch("/api/icqq/bots");
47
- const json = (await res.json()) as { success?: boolean; data?: BotRow[]; message?: string };
48
- if (!res.ok || !json.success) throw new Error(json.message || "加载失败");
49
- setBots(Array.isArray(json.data) ? json.data : []);
50
- } catch (e) {
51
- setErr((e as Error).message);
52
- setBots([]);
53
- } finally {
54
- setLoading(false);
55
- }
56
- }, []);
57
-
58
- useEffect(() => {
59
- if (tab === "overview") void loadBots();
60
- }, [tab, loadBots]);
61
-
62
- return (
63
- <div style={shell}>
64
- <div>
65
- <h1 style={title}>ICQQ 管理</h1>
66
- <p style={sub}>机器人概览与登录辅助(扫码 / 验证码 / 滑块)</p>
67
- </div>
68
-
69
- <div style={tabsRow}>
70
- <button type="button" style={tabBtn(tab === "overview")} onClick={() => setTab("overview")}>
71
- <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
72
- <Bot style={{ width: 16, height: 16 }} />
73
- 概览
74
- </span>
75
- </button>
76
- <button type="button" style={tabBtn(tab === "login")} onClick={() => setTab("login")}>
77
- <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
78
- <LogIn style={{ width: 16, height: 16 }} />
79
- 登录辅助
80
- </span>
81
- </button>
82
- </div>
83
-
84
- {tab === "overview" && (
85
- <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
86
- {err && (
87
- <div style={{ ...card, color: "hsl(var(--destructive))", fontSize: "0.875rem" }}>{err}</div>
88
- )}
89
- {loading ? (
90
- <div style={{ ...card, opacity: 0.6 }}>加载中…</div>
91
- ) : bots.length === 0 ? (
92
- <div style={{ ...card, textAlign: "center", color: "hsl(var(--muted-foreground))" }}>
93
- 暂无 ICQQ 机器人实例
94
- </div>
95
- ) : (
96
- <div
97
- style={{
98
- display: "grid",
99
- gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
100
- gap: "1rem",
101
- }}
102
- >
103
- {bots.map((b) => (
104
- <div key={b.name} style={card}>
105
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
106
- <strong style={{ fontSize: "1rem" }}>{b.name}</strong>
107
- <span
108
- style={{
109
- fontSize: "0.75rem",
110
- padding: "0.125rem 0.5rem",
111
- borderRadius: 999,
112
- background: b.connected ? "hsl(142 76% 36% / 0.15)" : "hsl(var(--muted))",
113
- color: b.connected ? "hsl(142 76% 30%)" : "hsl(var(--muted-foreground))",
114
- }}
115
- >
116
- {b.connected ? "在线" : "离线"}
117
- </span>
118
- </div>
119
- <div style={{ display: "flex", flexDirection: "column", gap: 6, fontSize: "0.8125rem", color: "hsl(var(--muted-foreground))" }}>
120
- <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
121
- <Users style={{ width: 14, height: 14 }} />
122
- 群 {b.groupCount} · 好友 {b.friendCount}
123
- </span>
124
- <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
125
- <Activity style={{ width: 14, height: 14 }} />
126
- 登录方式 {b.loginMode} · {b.status}
127
- </span>
128
- </div>
129
- </div>
130
- ))}
131
- </div>
132
- )}
133
- </div>
134
- )}
135
-
136
- {tab === "login" && <LoginAssistPanel />}
137
- </div>
138
- );
139
- }
@@ -1,293 +0,0 @@
1
- import { useCallback, useEffect, useState, type CSSProperties, type ReactNode } from "react";
2
- import {
3
- LogIn,
4
- QrCode,
5
- MessageSquare,
6
- MousePointer,
7
- Smartphone,
8
- AlertCircle,
9
- } from "lucide-react";
10
- import { apiFetch } from "./utils/api";
11
-
12
- const card: CSSProperties = {
13
- background: "hsl(var(--card))",
14
- color: "hsl(var(--card-foreground))",
15
- border: "1px solid hsl(var(--border))",
16
- borderRadius: "var(--radius)",
17
- };
18
- const muted: CSSProperties = { color: "hsl(var(--muted-foreground))", fontSize: "0.875rem" };
19
- const btn: CSSProperties = {
20
- padding: "0.375rem 0.75rem",
21
- borderRadius: "calc(var(--radius) - 2px)",
22
- border: "1px solid hsl(var(--border))",
23
- background: "hsl(var(--primary))",
24
- color: "hsl(var(--primary-foreground))",
25
- cursor: "pointer",
26
- fontSize: "0.875rem",
27
- };
28
- const btnOutline: CSSProperties = { ...btn, background: "transparent", color: "hsl(var(--foreground))" };
29
- const inputStyle: CSSProperties = {
30
- padding: "0.5rem 0.75rem",
31
- borderRadius: "calc(var(--radius) - 2px)",
32
- border: "1px solid hsl(var(--input))",
33
- background: "hsl(var(--background))",
34
- color: "hsl(var(--foreground))",
35
- maxWidth: "18rem",
36
- };
37
-
38
- interface PendingLoginTask {
39
- id: string;
40
- adapter: string;
41
- botId: string;
42
- type: string;
43
- payload?: {
44
- message?: string;
45
- image?: string;
46
- url?: string;
47
- [key: string]: unknown;
48
- };
49
- createdAt: number;
50
- }
51
-
52
- const POLL_MS = 2000;
53
-
54
- export default function LoginAssistPanel() {
55
- const [pending, setPending] = useState<PendingLoginTask[]>([]);
56
- const [loading, setLoading] = useState(true);
57
- const [error, setError] = useState<string | null>(null);
58
- const [submitting, setSubmitting] = useState<Record<string, boolean>>({});
59
- const [inputValues, setInputValues] = useState<Record<string, string>>({});
60
-
61
- const fetchPending = useCallback(async () => {
62
- try {
63
- const res = await apiFetch("/api/login-assist/pending");
64
- if (!res.ok) throw new Error("获取待办失败");
65
- const data = (await res.json()) as unknown;
66
- const list = Array.isArray(data) ? (data as PendingLoginTask[]) : [];
67
- setPending(list.filter((t) => t.adapter === "icqq"));
68
- setError(null);
69
- } catch (err) {
70
- setError((err as Error).message);
71
- } finally {
72
- setLoading(false);
73
- }
74
- }, []);
75
-
76
- useEffect(() => {
77
- fetchPending();
78
- const id = setInterval(fetchPending, POLL_MS);
79
- return () => clearInterval(id);
80
- }, [fetchPending]);
81
-
82
- const handleSubmit = async (id: string, value: string | Record<string, unknown>) => {
83
- setSubmitting((s) => ({ ...s, [id]: true }));
84
- try {
85
- const res = await apiFetch("/api/login-assist/submit", {
86
- method: "POST",
87
- headers: { "Content-Type": "application/json" },
88
- body: JSON.stringify({ id, value }),
89
- });
90
- if (!res.ok) throw new Error("提交失败");
91
- setInputValues((v) => {
92
- const next = { ...v };
93
- delete next[id];
94
- return next;
95
- });
96
- await fetchPending();
97
- } catch (err) {
98
- setError((err as Error).message);
99
- } finally {
100
- setSubmitting((s) => ({ ...s, [id]: false }));
101
- }
102
- };
103
-
104
- const handleCancel = async (id: string) => {
105
- try {
106
- const res = await apiFetch("/api/login-assist/cancel", {
107
- method: "POST",
108
- headers: { "Content-Type": "application/json" },
109
- body: JSON.stringify({ id }),
110
- });
111
- if (res.ok) await fetchPending();
112
- } catch {
113
- /* ignore */
114
- }
115
- };
116
-
117
- const typeIcon: Record<string, ReactNode> = {
118
- qrcode: <QrCode style={{ width: 16, height: 16 }} />,
119
- sms: <MessageSquare style={{ width: 16, height: 16 }} />,
120
- device: <Smartphone style={{ width: 16, height: 16 }} />,
121
- slider: <MousePointer style={{ width: 16, height: 16 }} />,
122
- };
123
- const typeLabel: Record<string, string> = {
124
- qrcode: "扫码登录",
125
- sms: "短信验证码",
126
- device: "设备验证",
127
- slider: "滑块验证",
128
- other: "其他",
129
- };
130
-
131
- if (loading && pending.length === 0) {
132
- return (
133
- <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
134
- <div style={{ ...card, height: "2rem", width: "12rem", opacity: 0.5 }} />
135
- <div style={{ ...card, height: "8rem", opacity: 0.5 }} />
136
- </div>
137
- );
138
- }
139
-
140
- return (
141
- <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
142
- <p style={muted}>
143
- 扫码、短信或滑块等验证会出现在下方;刷新页面后仍可继续处理(仅 ICQQ 待办)。
144
- </p>
145
-
146
- {error && (
147
- <div
148
- style={{
149
- ...card,
150
- borderColor: "hsl(var(--destructive))",
151
- color: "hsl(var(--destructive))",
152
- padding: "0.75rem",
153
- display: "flex",
154
- alignItems: "center",
155
- gap: "0.5rem",
156
- }}
157
- >
158
- <AlertCircle style={{ width: 16, height: 16, flexShrink: 0 }} />
159
- <span style={{ fontSize: "0.875rem" }}>{error}</span>
160
- </div>
161
- )}
162
-
163
- {pending.length === 0 ? (
164
- <div style={{ ...card, padding: "3rem", textAlign: "center" }}>
165
- <LogIn style={{ width: 64, height: 64, margin: "0 auto 1rem", opacity: 0.25 }} />
166
- <h3 style={{ margin: "0 0 0.5rem", fontSize: "1.125rem" }}>暂无待办</h3>
167
- <p style={{ ...muted, margin: 0 }}>机器人需要验证时,待办将显示在此处</p>
168
- </div>
169
- ) : (
170
- <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
171
- {pending.map((task) => (
172
- <div key={task.id} style={{ ...card, padding: "1rem" }}>
173
- <div
174
- style={{
175
- display: "flex",
176
- justifyContent: "space-between",
177
- alignItems: "flex-start",
178
- marginBottom: "0.5rem",
179
- flexWrap: "wrap",
180
- gap: "0.5rem",
181
- }}
182
- >
183
- <div style={{ display: "flex", alignItems: "center", gap: "0.5rem", fontWeight: 600 }}>
184
- {typeIcon[task.type] ?? <LogIn style={{ width: 16, height: 16 }} />}
185
- {typeLabel[task.type] ?? task.type}
186
- </div>
187
- <div style={{ display: "flex", gap: "0.5rem", fontSize: "0.75rem" }}>
188
- <span
189
- style={{
190
- padding: "0.125rem 0.5rem",
191
- borderRadius: 4,
192
- border: "1px solid hsl(var(--border))",
193
- }}
194
- >
195
- {task.adapter}
196
- </span>
197
- <span
198
- style={{
199
- padding: "0.125rem 0.5rem",
200
- borderRadius: 4,
201
- background: "hsl(var(--muted))",
202
- }}
203
- >
204
- {task.botId}
205
- </span>
206
- </div>
207
- </div>
208
- {task.payload?.message && <p style={{ ...muted, margin: "0 0 1rem" }}>{task.payload.message}</p>}
209
-
210
- {task.type === "qrcode" && task.payload?.image && (
211
- <div
212
- style={{
213
- display: "flex",
214
- justifyContent: "center",
215
- padding: "1rem",
216
- background: "hsl(var(--muted) / 0.3)",
217
- borderRadius: "var(--radius)",
218
- marginBottom: "1rem",
219
- }}
220
- >
221
- <img
222
- src={String(task.payload.image)}
223
- alt="登录二维码"
224
- style={{ maxWidth: 200, width: "100%", height: "auto" }}
225
- />
226
- </div>
227
- )}
228
-
229
- {task.type === "slider" && task.payload?.url && (
230
- <p style={{ fontSize: "0.875rem", wordBreak: "break-all", marginBottom: "1rem" }}>
231
- <span style={muted}>滑块链接:</span>{" "}
232
- <a
233
- href={String(task.payload.url)}
234
- target="_blank"
235
- rel="noopener noreferrer"
236
- style={{ color: "hsl(var(--primary))" }}
237
- >
238
- {String(task.payload.url)}
239
- </a>
240
- </p>
241
- )}
242
-
243
- {(task.type === "sms" || task.type === "device" || task.type === "slider") && (
244
- <div style={{ display: "flex", flexWrap: "wrap", gap: "0.5rem", alignItems: "center" }}>
245
- <input
246
- style={inputStyle}
247
- placeholder={task.type === "slider" ? "输入 ticket" : "输入验证码"}
248
- value={inputValues[task.id] ?? ""}
249
- onChange={(e) => setInputValues((v) => ({ ...v, [task.id]: e.target.value }))}
250
- onKeyDown={(e) => {
251
- if (e.key === "Enter") {
252
- const val = inputValues[task.id]?.trim();
253
- if (val)
254
- void handleSubmit(task.id, task.type === "slider" ? { ticket: val } : val);
255
- }
256
- }}
257
- />
258
- <button
259
- type="button"
260
- style={btn}
261
- disabled={submitting[task.id] || !inputValues[task.id]?.trim()}
262
- onClick={() => {
263
- const val = inputValues[task.id]?.trim();
264
- if (val) void handleSubmit(task.id, task.type === "slider" ? { ticket: val } : val);
265
- }}
266
- >
267
- {submitting[task.id] ? "提交中…" : "提交"}
268
- </button>
269
- </div>
270
- )}
271
-
272
- {task.type === "qrcode" && (
273
- <div style={{ display: "flex", gap: "0.5rem", marginTop: "0.75rem" }}>
274
- <button
275
- type="button"
276
- style={btn}
277
- disabled={!!submitting[task.id]}
278
- onClick={() => void handleSubmit(task.id, { done: true })}
279
- >
280
- {submitting[task.id] ? "提交中…" : "我已扫码"}
281
- </button>
282
- <button type="button" style={btnOutline} onClick={() => void handleCancel(task.id)}>
283
- 取消
284
- </button>
285
- </div>
286
- )}
287
- </div>
288
- ))}
289
- </div>
290
- )}
291
- </div>
292
- );
293
- }
package/client/index.tsx DELETED
@@ -1,11 +0,0 @@
1
- import { addPage } from '@zhin.js/client'
2
- import { Bot } from 'lucide-react'
3
- import ICQQManagement from './ICQQManagement'
4
-
5
- addPage({
6
- key: 'icqq-management',
7
- path: '/icqq',
8
- title: 'ICQQ管理',
9
- icon: <Bot className="w-5 h-5" />,
10
- element: <ICQQManagement/>
11
- })
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "@zhin.js/console/browser.tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../dist"
5
- },
6
- "include": ["./**/*"]
7
- }
@@ -1,20 +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
-
12
- const res = await fetch(input, { ...init, headers });
13
-
14
- if (res.status === 401) {
15
- localStorage.removeItem(TOKEN_KEY);
16
- window.dispatchEvent(new CustomEvent("zhin:auth-required"));
17
- }
18
-
19
- return res;
20
- }
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- import{addPage as e}from"@zhin.js/client";import{Activity as t,AlertCircle as n,Bot as r,LogIn as i,MessageSquare as a,MousePointer as o,QrCode as s,Smartphone as c,Users as l}from"lucide-react";import{useCallback as u,useEffect as d,useState as f}from"react";import{jsx as p,jsxs as m}from"react/jsx-runtime";var h="zhin_api_token";function g(){return localStorage.getItem(h)}async function _(e2,t2){let n2=g(),r2=new Headers(t2?.headers);n2&&r2.set("Authorization",`Bearer ${n2}`);let i2=await fetch(e2,{...t2,headers:r2});return i2.status===401&&(localStorage.removeItem(h),window.dispatchEvent(new CustomEvent("zhin:auth-required"))),i2}var v={background:"hsl(var(--card))",color:"hsl(var(--card-foreground))",border:"1px solid hsl(var(--border))",borderRadius:"var(--radius)"},y={color:"hsl(var(--muted-foreground))",fontSize:"0.875rem"},b={padding:"0.375rem 0.75rem",borderRadius:"calc(var(--radius) - 2px)",border:"1px solid hsl(var(--border))",background:"hsl(var(--primary))",color:"hsl(var(--primary-foreground))",cursor:"pointer",fontSize:"0.875rem"},x={...b,background:"transparent",color:"hsl(var(--foreground))"},S={padding:"0.5rem 0.75rem",borderRadius:"calc(var(--radius) - 2px)",border:"1px solid hsl(var(--input))",background:"hsl(var(--background))",color:"hsl(var(--foreground))",maxWidth:"18rem"},C=2e3;function w(){let[e2,t2]=f([]),[r2,l2]=f(true),[h2,g2]=f(null),[w2,T2]=f({}),[E2,D2]=f({}),O2=u(async()=>{try{let e3=await _("/api/login-assist/pending");if(!e3.ok)throw Error("获取待办失败");let n2=await e3.json();t2((Array.isArray(n2)?n2:[]).filter(e4=>e4.adapter==="icqq")),g2(null)}catch(e3){g2(e3.message)}finally{l2(false)}},[]);d(()=>{O2();let e3=setInterval(O2,C);return()=>clearInterval(e3)},[O2]);let k2=async(e3,t3)=>{T2(t4=>({...t4,[e3]:true}));try{if(!(await _("/api/login-assist/submit",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e3,value:t3})})).ok)throw Error("提交失败");D2(t4=>{let n2={...t4};return delete n2[e3],n2}),await O2()}catch(e4){g2(e4.message)}finally{T2(t4=>({...t4,[e3]:false}))}},A2=async e3=>{try{(await _("/api/login-assist/cancel",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e3})})).ok&&await O2()}catch{}},j2={qrcode:p(s,{style:{width:16,height:16}}),sms:p(a,{style:{width:16,height:16}}),device:p(c,{style:{width:16,height:16}}),slider:p(o,{style:{width:16,height:16}})},M={qrcode:"扫码登录",sms:"短信验证码",device:"设备验证",slider:"滑块验证",other:"其他"};return r2&&e2.length===0?m("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[p("div",{style:{...v,height:"2rem",width:"12rem",opacity:.5}}),p("div",{style:{...v,height:"8rem",opacity:.5}})]}):m("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[p("p",{style:y,children:"扫码、短信或滑块等验证会出现在下方;刷新页面后仍可继续处理(仅 ICQQ 待办)。"}),h2&&m("div",{style:{...v,borderColor:"hsl(var(--destructive))",color:"hsl(var(--destructive))",padding:"0.75rem",display:"flex",alignItems:"center",gap:"0.5rem"},children:[p(n,{style:{width:16,height:16,flexShrink:0}}),p("span",{style:{fontSize:"0.875rem"},children:h2})]}),e2.length===0?m("div",{style:{...v,padding:"3rem",textAlign:"center"},children:[p(i,{style:{width:64,height:64,margin:"0 auto 1rem",opacity:.25}}),p("h3",{style:{margin:"0 0 0.5rem",fontSize:"1.125rem"},children:"暂无待办"}),p("p",{style:{...y,margin:0},children:"机器人需要验证时,待办将显示在此处"})]}):p("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:e2.map(e3=>m("div",{style:{...v,padding:"1rem"},children:[m("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"flex-start",marginBottom:"0.5rem",flexWrap:"wrap",gap:"0.5rem"},children:[m("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",fontWeight:600},children:[j2[e3.type]??p(i,{style:{width:16,height:16}}),M[e3.type]??e3.type]}),m("div",{style:{display:"flex",gap:"0.5rem",fontSize:"0.75rem"},children:[p("span",{style:{padding:"0.125rem 0.5rem",borderRadius:4,border:"1px solid hsl(var(--border))"},children:e3.adapter}),p("span",{style:{padding:"0.125rem 0.5rem",borderRadius:4,background:"hsl(var(--muted))"},children:e3.botId})]})]}),e3.payload?.message&&p("p",{style:{...y,margin:"0 0 1rem"},children:e3.payload.message}),e3.type==="qrcode"&&e3.payload?.image&&p("div",{style:{display:"flex",justifyContent:"center",padding:"1rem",background:"hsl(var(--muted) / 0.3)",borderRadius:"var(--radius)",marginBottom:"1rem"},children:p("img",{src:String(e3.payload.image),alt:"登录二维码",style:{maxWidth:200,width:"100%",height:"auto"}})}),e3.type==="slider"&&e3.payload?.url&&m("p",{style:{fontSize:"0.875rem",wordBreak:"break-all",marginBottom:"1rem"},children:[p("span",{style:y,children:"滑块链接:"})," ",p("a",{href:String(e3.payload.url),target:"_blank",rel:"noopener noreferrer",style:{color:"hsl(var(--primary))"},children:String(e3.payload.url)})]}),(e3.type==="sms"||e3.type==="device"||e3.type==="slider")&&m("div",{style:{display:"flex",flexWrap:"wrap",gap:"0.5rem",alignItems:"center"},children:[p("input",{style:S,placeholder:e3.type==="slider"?"输入 ticket":"输入验证码",value:E2[e3.id]??"",onChange:t3=>D2(n2=>({...n2,[e3.id]:t3.target.value})),onKeyDown:t3=>{if(t3.key==="Enter"){let t4=E2[e3.id]?.trim();t4&&k2(e3.id,e3.type==="slider"?{ticket:t4}:t4)}}}),p("button",{type:"button",style:b,disabled:w2[e3.id]||!E2[e3.id]?.trim(),onClick:()=>{let t3=E2[e3.id]?.trim();t3&&k2(e3.id,e3.type==="slider"?{ticket:t3}:t3)},children:w2[e3.id]?"提交中…":"提交"})]}),e3.type==="qrcode"&&m("div",{style:{display:"flex",gap:"0.5rem",marginTop:"0.75rem"},children:[p("button",{type:"button",style:b,disabled:!!w2[e3.id],onClick:()=>void k2(e3.id,{done:true}),children:w2[e3.id]?"提交中…":"我已扫码"}),p("button",{type:"button",style:x,onClick:()=>void A2(e3.id),children:"取消"})]})]},e3.id))})]})}var T={display:"flex",flexDirection:"column",gap:"1.25rem"},E={fontSize:"1.5rem",fontWeight:700,margin:0},D={color:"hsl(var(--muted-foreground))",fontSize:"0.875rem",margin:"0.25rem 0 0"},O={display:"flex",gap:"0.25rem",borderBottom:"1px solid hsl(var(--border))",paddingBottom:2},k=e2=>({padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,border:"none",borderRadius:"calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0 0",cursor:"pointer",background:e2?"hsl(var(--muted))":"transparent",color:e2?"hsl(var(--foreground))":"hsl(var(--muted-foreground))"}),A={background:"hsl(var(--card))",border:"1px solid hsl(var(--border))",borderRadius:"var(--radius)",padding:"1rem"};function j(){let[e2,n2]=f("overview"),[a2,o2]=f([]),[s2,c2]=f(true),[h2,g2]=f(null),v2=u(async()=>{c2(true),g2(null);try{let e3=await _("/api/icqq/bots"),t2=await e3.json();if(!e3.ok||!t2.success)throw Error(t2.message||"加载失败");o2(Array.isArray(t2.data)?t2.data:[])}catch(e3){g2(e3.message),o2([])}finally{c2(false)}},[]);return d(()=>{e2==="overview"&&v2()},[e2,v2]),m("div",{style:T,children:[m("div",{children:[p("h1",{style:E,children:"ICQQ 管理"}),p("p",{style:D,children:"机器人概览与登录辅助(扫码 / 验证码 / 滑块)"})]}),m("div",{style:O,children:[p("button",{type:"button",style:k(e2==="overview"),onClick:()=>n2("overview"),children:m("span",{style:{display:"inline-flex",alignItems:"center",gap:6},children:[p(r,{style:{width:16,height:16}}),"概览"]})}),p("button",{type:"button",style:k(e2==="login"),onClick:()=>n2("login"),children:m("span",{style:{display:"inline-flex",alignItems:"center",gap:6},children:[p(i,{style:{width:16,height:16}}),"登录辅助"]})})]}),e2==="overview"&&m("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[h2&&p("div",{style:{...A,color:"hsl(var(--destructive))",fontSize:"0.875rem"},children:h2}),s2?p("div",{style:{...A,opacity:.6},children:"加载中…"}):a2.length===0?p("div",{style:{...A,textAlign:"center",color:"hsl(var(--muted-foreground))"},children:"暂无 ICQQ 机器人实例"}):p("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(260px, 1fr))",gap:"1rem"},children:a2.map(e3=>m("div",{style:A,children:[m("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:8},children:[p("strong",{style:{fontSize:"1rem"},children:e3.name}),p("span",{style:{fontSize:"0.75rem",padding:"0.125rem 0.5rem",borderRadius:999,background:e3.connected?"hsl(142 76% 36% / 0.15)":"hsl(var(--muted))",color:e3.connected?"hsl(142 76% 30%)":"hsl(var(--muted-foreground))"},children:e3.connected?"在线":"离线"})]}),m("div",{style:{display:"flex",flexDirection:"column",gap:6,fontSize:"0.8125rem",color:"hsl(var(--muted-foreground))"},children:[m("span",{style:{display:"flex",alignItems:"center",gap:6},children:[p(l,{style:{width:14,height:14}}),"群 ",e3.groupCount," · 好友 ",e3.friendCount]}),m("span",{style:{display:"flex",alignItems:"center",gap:6},children:[p(t,{style:{width:14,height:14}}),"登录方式 ",e3.loginMode," · ",e3.status]})]})]},e3.name))})]}),e2==="login"&&p(w,{})]})}e({key:"icqq-management",path:"/icqq",title:"ICQQ管理",icon:p(r,{className:"w-5 h-5"}),element:p(j,{})});
package/lib/adapter.d.ts DELETED
@@ -1,34 +0,0 @@
1
- import { Adapter, Plugin } from "zhin.js";
2
- import { IcqqBot } from "./bot.js";
3
- import type { IcqqBotConfig } from "./types.js";
4
- export declare class IcqqAdapter extends Adapter<IcqqBot> {
5
- constructor(plugin: Plugin);
6
- createBot(config: IcqqBotConfig): IcqqBot;
7
- kickMember(botId: string, sceneId: string, userId: string): Promise<boolean>;
8
- muteMember(botId: string, sceneId: string, userId: string, duration?: number): Promise<boolean>;
9
- muteAll(botId: string, sceneId: string, enable?: boolean): Promise<boolean>;
10
- setAdmin(botId: string, sceneId: string, userId: string, enable?: boolean): Promise<boolean>;
11
- setMemberNickname(botId: string, sceneId: string, userId: string, nickname: string): Promise<boolean>;
12
- setGroupName(botId: string, sceneId: string, name: string): Promise<boolean>;
13
- listMembers(botId: string, sceneId: string): Promise<{
14
- members: {
15
- user_id: number;
16
- nickname: string;
17
- card: string;
18
- role: import("@icqqjs/icqq").GroupRole;
19
- title: string;
20
- }[];
21
- count: number;
22
- }>;
23
- start(): Promise<void>;
24
- /**
25
- * 注册 ICQQ 平台特有工具(头衔、戳一戳、群公告等)
26
- */
27
- private registerIcqqPlatformTools;
28
- /**
29
- * 检查执行上下文中的权限
30
- * 双重验证:优先从 ToolContext 获取权限级别,其次从消息发送者信息获取
31
- */
32
- private checkPermission;
33
- }
34
- //# sourceMappingURL=adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,OAAO,EACP,MAAM,EAIP,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AAEhE,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,UAAU,CACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,SAAM;IAOV,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAO;IAMrD,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,UAAO;IAOT,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;IAOZ,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAMzD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;IAgB1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAuSjC;;;OAGG;IACH,OAAO,CAAC,eAAe;CA8CxB"}