@zhin.js/adapter-icqq 2.0.3 → 2.0.5

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
+ ## 2.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - ba30934: fix: web 优化
8
+ - Updated dependencies [ba30934]
9
+ - @zhin.js/console@2.0.5
10
+ - @zhin.js/http@1.0.58
11
+ - zhin.js@1.0.63
12
+ - @zhin.js/core@1.1.5
13
+
14
+ ## 2.0.4
15
+
16
+ ### Patch Changes
17
+
18
+ - zhin.js@1.0.62
19
+ - @zhin.js/console@2.0.4
20
+ - @zhin.js/http@1.0.57
21
+ - @zhin.js/core@1.1.4
22
+
3
23
  ## 2.0.3
4
24
 
5
25
  ### Patch Changes
@@ -1,29 +1,8 @@
1
- import { useCallback, useEffect, useState, type CSSProperties } from "react";
2
- import { Activity, Bot, LogIn, Users } from "lucide-react";
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { Activity, Bot, LogIn, Users, RefreshCw, Wifi, WifiOff, Loader2 } from "lucide-react";
3
3
  import LoginAssistPanel from "./LoginAssistPanel";
4
4
  import { apiFetch } from "./utils/api";
5
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
6
  interface BotRow {
28
7
  name: string;
29
8
  connected: boolean;
@@ -33,8 +12,10 @@ interface BotRow {
33
12
  status: string;
34
13
  }
35
14
 
15
+ type Tab = "overview" | "login";
16
+
36
17
  export default function ICQQManagement() {
37
- const [tab, setTab] = useState<"overview" | "login">("overview");
18
+ const [tab, setTab] = useState<Tab>("overview");
38
19
  const [bots, setBots] = useState<BotRow[]>([]);
39
20
  const [loading, setLoading] = useState(true);
40
21
  const [err, setErr] = useState<string | null>(null);
@@ -60,79 +41,106 @@ export default function ICQQManagement() {
60
41
  }, [tab, loadBots]);
61
42
 
62
43
  return (
63
- <div style={shell}>
64
- <div>
65
- <h1 style={title}>ICQQ 管理</h1>
66
- <p style={sub}>机器人概览与登录辅助(扫码 / 验证码 / 滑块)</p>
44
+ <div className="p-6 max-w-5xl mx-auto space-y-5">
45
+ {/* Header */}
46
+ <div className="flex items-center justify-between">
47
+ <div>
48
+ <h1 className="text-2xl font-bold flex items-center gap-2">
49
+ <Bot className="w-6 h-6" /> ICQQ 管理
50
+ </h1>
51
+ <p className="text-sm text-muted-foreground mt-1">
52
+ 机器人概览与登录辅助(扫码 / 验证码 / 滑块)
53
+ </p>
54
+ </div>
55
+ {tab === "overview" && (
56
+ <button
57
+ onClick={loadBots}
58
+ disabled={loading}
59
+ className="flex items-center gap-1 px-3 py-1.5 rounded bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50 text-sm"
60
+ >
61
+ <RefreshCw className={`w-4 h-4 ${loading ? "animate-spin" : ""}`} /> 刷新
62
+ </button>
63
+ )}
67
64
  </div>
68
65
 
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
- 概览
66
+ {/* Tabs */}
67
+ <div className="flex gap-1 border-b">
68
+ <button
69
+ onClick={() => setTab("overview")}
70
+ className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
71
+ tab === "overview"
72
+ ? "border-blue-500 text-blue-600"
73
+ : "border-transparent text-gray-500 hover:text-gray-700"
74
+ }`}
75
+ >
76
+ <span className="flex items-center gap-1.5">
77
+ <Bot className="w-4 h-4" /> 概览
74
78
  </span>
75
79
  </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
+ <button
81
+ onClick={() => setTab("login")}
82
+ className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
83
+ tab === "login"
84
+ ? "border-blue-500 text-blue-600"
85
+ : "border-transparent text-gray-500 hover:text-gray-700"
86
+ }`}
87
+ >
88
+ <span className="flex items-center gap-1.5">
89
+ <LogIn className="w-4 h-4" /> 登录辅助
80
90
  </span>
81
91
  </button>
82
92
  </div>
83
93
 
94
+ {/* Overview Tab */}
84
95
  {tab === "overview" && (
85
- <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
96
+ <>
86
97
  {err && (
87
- <div style={{ ...card, color: "hsl(var(--destructive))", fontSize: "0.875rem" }}>{err}</div>
98
+ <div className="p-3 bg-red-50 text-red-600 rounded border border-red-200 text-sm dark:bg-red-900/20 dark:text-red-400 dark:border-red-800">
99
+ {err}
100
+ </div>
88
101
  )}
89
102
  {loading ? (
90
- <div style={{ ...card, opacity: 0.6 }}>加载中…</div>
103
+ <div className="flex justify-center py-12">
104
+ <Loader2 className="w-6 h-6 animate-spin text-blue-500" />
105
+ </div>
91
106
  ) : bots.length === 0 ? (
92
- <div style={{ ...card, textAlign: "center", color: "hsl(var(--muted-foreground))" }}>
107
+ <div className="text-center text-muted-foreground py-12">
93
108
  暂无 ICQQ 机器人实例
94
109
  </div>
95
110
  ) : (
96
- <div
97
- style={{
98
- display: "grid",
99
- gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
100
- gap: "1rem",
101
- }}
102
- >
111
+ <div className="grid gap-4 md:grid-cols-2">
103
112
  {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>
113
+ <div key={b.name} className="border rounded-lg p-4 bg-card shadow-sm">
114
+ <div className="flex items-center justify-between mb-3">
115
+ <span className="font-medium text-lg">{b.name}</span>
116
+ {b.connected ? (
117
+ <span className="flex items-center gap-1 text-green-600 text-sm">
118
+ <Wifi className="w-4 h-4" /> 在线
119
+ </span>
120
+ ) : (
121
+ <span className="flex items-center gap-1 text-gray-400 text-sm">
122
+ <WifiOff className="w-4 h-4" /> 离线
123
+ </span>
124
+ )}
118
125
  </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 }} />
126
+ <div className="space-y-2 text-sm text-muted-foreground">
127
+ <div className="flex items-center gap-1.5">
128
+ <Users className="w-3.5 h-3.5" />
122
129
  群 {b.groupCount} · 好友 {b.friendCount}
123
- </span>
124
- <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
125
- <Activity style={{ width: 14, height: 14 }} />
130
+ </div>
131
+ <div className="flex items-center gap-1.5">
132
+ <Activity className="w-3.5 h-3.5" />
126
133
  登录方式 {b.loginMode} · {b.status}
127
- </span>
134
+ </div>
128
135
  </div>
129
136
  </div>
130
137
  ))}
131
138
  </div>
132
139
  )}
133
- </div>
140
+ </>
134
141
  )}
135
142
 
143
+ {/* Login Tab */}
136
144
  {tab === "login" && <LoginAssistPanel />}
137
145
  </div>
138
146
  );
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useState, type CSSProperties, type ReactNode } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import {
3
3
  LogIn,
4
4
  QrCode,
@@ -6,35 +6,11 @@ import {
6
6
  MousePointer,
7
7
  Smartphone,
8
8
  AlertCircle,
9
+ Loader2,
10
+ X,
9
11
  } from "lucide-react";
10
12
  import { apiFetch } from "./utils/api";
11
13
 
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
14
  interface PendingLoginTask {
39
15
  id: string;
40
16
  adapter: string;
@@ -49,7 +25,15 @@ interface PendingLoginTask {
49
25
  createdAt: number;
50
26
  }
51
27
 
52
- const POLL_MS = 2000;
28
+ const POLL_BASE_MS = 2_000;
29
+ const POLL_MAX_MS = 15_000;
30
+
31
+ const TYPE_META: Record<string, { icon: React.ReactNode; label: string }> = {
32
+ qrcode: { icon: <QrCode className="w-4 h-4" />, label: "扫码登录" },
33
+ sms: { icon: <MessageSquare className="w-4 h-4" />, label: "短信验证码" },
34
+ device: { icon: <Smartphone className="w-4 h-4" />, label: "设备验证" },
35
+ slider: { icon: <MousePointer className="w-4 h-4" />, label: "滑块验证" },
36
+ };
53
37
 
54
38
  export default function LoginAssistPanel() {
55
39
  const [pending, setPending] = useState<PendingLoginTask[]>([]);
@@ -58,27 +42,45 @@ export default function LoginAssistPanel() {
58
42
  const [submitting, setSubmitting] = useState<Record<string, boolean>>({});
59
43
  const [inputValues, setInputValues] = useState<Record<string, string>>({});
60
44
 
45
+ /* ── Exponential-backoff polling ── */
46
+ const consecutiveEmptyRef = useRef(0);
47
+ const timerRef = useRef<ReturnType<typeof setTimeout>>();
48
+
61
49
  const fetchPending = useCallback(async () => {
62
50
  try {
63
51
  const res = await apiFetch("/api/login-assist/pending");
64
52
  if (!res.ok) throw new Error("获取待办失败");
65
53
  const data = (await res.json()) as unknown;
66
54
  const list = Array.isArray(data) ? (data as PendingLoginTask[]) : [];
67
- setPending(list.filter((t) => t.adapter === "icqq"));
55
+ const filtered = list.filter((t) => t.adapter === "icqq");
56
+ setPending(filtered);
68
57
  setError(null);
58
+ consecutiveEmptyRef.current = filtered.length > 0 ? 0 : consecutiveEmptyRef.current + 1;
69
59
  } catch (err) {
70
60
  setError((err as Error).message);
61
+ consecutiveEmptyRef.current++;
71
62
  } finally {
72
63
  setLoading(false);
73
64
  }
74
65
  }, []);
75
66
 
76
- useEffect(() => {
77
- fetchPending();
78
- const id = setInterval(fetchPending, POLL_MS);
79
- return () => clearInterval(id);
67
+ const scheduleNext = useCallback(() => {
68
+ const delay = Math.min(
69
+ POLL_BASE_MS * Math.pow(1.5, consecutiveEmptyRef.current),
70
+ POLL_MAX_MS,
71
+ );
72
+ timerRef.current = setTimeout(async () => {
73
+ await fetchPending();
74
+ scheduleNext();
75
+ }, delay);
80
76
  }, [fetchPending]);
81
77
 
78
+ useEffect(() => {
79
+ fetchPending().then(scheduleNext);
80
+ return () => clearTimeout(timerRef.current);
81
+ }, [fetchPending, scheduleNext]);
82
+
83
+ /* ── Actions ── */
82
84
  const handleSubmit = async (id: string, value: string | Record<string, unknown>) => {
83
85
  setSubmitting((s) => ({ ...s, [id]: true }));
84
86
  try {
@@ -93,6 +95,7 @@ export default function LoginAssistPanel() {
93
95
  delete next[id];
94
96
  return next;
95
97
  });
98
+ consecutiveEmptyRef.current = 0;
96
99
  await fetchPending();
97
100
  } catch (err) {
98
101
  setError((err as Error).message);
@@ -114,178 +117,147 @@ export default function LoginAssistPanel() {
114
117
  }
115
118
  };
116
119
 
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
-
120
+ /* ── Loading skeleton ── */
131
121
  if (loading && pending.length === 0) {
132
122
  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 }} />
123
+ <div className="flex justify-center py-12">
124
+ <Loader2 className="w-6 h-6 animate-spin text-blue-500" />
136
125
  </div>
137
126
  );
138
127
  }
139
128
 
140
129
  return (
141
- <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
142
- <p style={muted}>
130
+ <div className="space-y-4">
131
+ <p className="text-sm text-muted-foreground">
143
132
  扫码、短信或滑块等验证会出现在下方;刷新页面后仍可继续处理(仅 ICQQ 待办)。
144
133
  </p>
145
134
 
146
135
  {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>
136
+ <div className="flex items-center gap-2 p-3 rounded border border-red-200 bg-red-50 text-red-600 text-sm dark:bg-red-900/20 dark:text-red-400 dark:border-red-800">
137
+ <AlertCircle className="w-4 h-4 shrink-0" />
138
+ <span>{error}</span>
139
+ <button onClick={() => setError(null)} className="ml-auto">
140
+ <X className="w-3.5 h-3.5" />
141
+ </button>
160
142
  </div>
161
143
  )}
162
144
 
163
145
  {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>
146
+ <div className="border rounded-lg bg-card p-12 text-center">
147
+ <LogIn className="w-16 h-16 mx-auto mb-4 opacity-25" />
148
+ <h3 className="text-lg font-semibold mb-1">暂无待办</h3>
149
+ <p className="text-sm text-muted-foreground">机器人需要验证时,待办将显示在此处</p>
168
150
  </div>
169
151
  ) : (
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}
152
+ <div className="space-y-4">
153
+ {pending.map((task) => {
154
+ const meta = TYPE_META[task.type] ?? {
155
+ icon: <LogIn className="w-4 h-4" />,
156
+ label: task.type,
157
+ };
158
+ return (
159
+ <div key={task.id} className="border rounded-lg bg-card p-4 shadow-sm">
160
+ {/* Header */}
161
+ <div className="flex items-start justify-between flex-wrap gap-2 mb-2">
162
+ <div className="flex items-center gap-2 font-semibold">
163
+ {meta.icon} {meta.label}
164
+ </div>
165
+ <div className="flex gap-1 text-xs">
166
+ <span className="px-2 py-0.5 rounded border">{task.adapter}</span>
167
+ <span className="px-2 py-0.5 rounded bg-muted">{task.botId}</span>
168
+ </div>
186
169
  </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
170
 
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
- )}
171
+ {/* Message */}
172
+ {task.payload?.message && (
173
+ <p className="text-sm text-muted-foreground mb-3">{task.payload.message}</p>
174
+ )}
228
175
 
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
- )}
176
+ {/* QR Code */}
177
+ {task.type === "qrcode" && task.payload?.image && (
178
+ <div className="flex justify-center p-4 bg-muted/30 rounded-lg mb-3">
179
+ <img
180
+ src={String(task.payload.image)}
181
+ alt="登录二维码"
182
+ className="max-w-[200px] w-full h-auto"
183
+ />
184
+ </div>
185
+ )}
242
186
 
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") {
187
+ {/* Slider URL */}
188
+ {task.type === "slider" && task.payload?.url && (
189
+ <p className="text-sm break-all mb-3">
190
+ <span className="text-muted-foreground">滑块链接:</span>{" "}
191
+ <a
192
+ href={String(task.payload.url)}
193
+ target="_blank"
194
+ rel="noopener noreferrer"
195
+ className="text-primary hover:underline"
196
+ >
197
+ {String(task.payload.url)}
198
+ </a>
199
+ </p>
200
+ )}
201
+
202
+ {/* Text input (sms / device / slider ticket) */}
203
+ {(task.type === "sms" || task.type === "device" || task.type === "slider") && (
204
+ <div className="flex flex-wrap gap-2 items-center">
205
+ <input
206
+ className="h-9 px-3 rounded-md border border-input bg-background text-sm max-w-[18rem] flex-1"
207
+ placeholder={task.type === "slider" ? "输入 ticket" : "输入验证码"}
208
+ value={inputValues[task.id] ?? ""}
209
+ onChange={(e) =>
210
+ setInputValues((v) => ({ ...v, [task.id]: e.target.value }))
211
+ }
212
+ onKeyDown={(e) => {
213
+ if (e.key === "Enter") {
214
+ const val = inputValues[task.id]?.trim();
215
+ if (val)
216
+ void handleSubmit(
217
+ task.id,
218
+ task.type === "slider" ? { ticket: val } : val,
219
+ );
220
+ }
221
+ }}
222
+ />
223
+ <button
224
+ className="h-9 px-4 rounded-md bg-primary text-primary-foreground text-sm disabled:opacity-50"
225
+ disabled={submitting[task.id] || !inputValues[task.id]?.trim()}
226
+ onClick={() => {
252
227
  const val = inputValues[task.id]?.trim();
253
228
  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
- )}
229
+ void handleSubmit(
230
+ task.id,
231
+ task.type === "slider" ? { ticket: val } : val,
232
+ );
233
+ }}
234
+ >
235
+ {submitting[task.id] ? "提交中…" : "提交"}
236
+ </button>
237
+ </div>
238
+ )}
271
239
 
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
- ))}
240
+ {/* QR code actions */}
241
+ {task.type === "qrcode" && (
242
+ <div className="flex gap-2 mt-3">
243
+ <button
244
+ className="h-9 px-4 rounded-md bg-primary text-primary-foreground text-sm disabled:opacity-50"
245
+ disabled={!!submitting[task.id]}
246
+ onClick={() => void handleSubmit(task.id, { done: true })}
247
+ >
248
+ {submitting[task.id] ? "提交中…" : "我已扫码"}
249
+ </button>
250
+ <button
251
+ className="h-9 px-4 rounded-md border text-sm hover:bg-accent transition-colors"
252
+ onClick={() => void handleCancel(task.id)}
253
+ >
254
+ 取消
255
+ </button>
256
+ </div>
257
+ )}
258
+ </div>
259
+ );
260
+ })}
289
261
  </div>
290
262
  )}
291
263
  </div>
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "@zhin.js/console/browser.tsconfig.json",
2
+ "extends": "../node_modules/@zhin.js/console/browser.tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "outDir": "../dist"
5
5
  },
package/dist/index.js CHANGED
@@ -1 +1 @@
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,{})});
1
+ import{addPage as e}from"@zhin.js/client";import{Activity as t,AlertCircle as n,Bot as r,Loader2 as i,LogIn as a,MessageSquare as o,MousePointer as s,QrCode as c,RefreshCw as l,Smartphone as u,Users as d,Wifi as f,WifiOff as p,X as m}from"lucide-react";import{useCallback as h,useEffect as g,useRef as _,useState as v}from"react";import{Fragment as y,jsx as b,jsxs as x}from"react/jsx-runtime";var S="zhin_api_token";function C(){return localStorage.getItem(S)}async function w(e2,t2){let n2=C(),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(S),window.dispatchEvent(new CustomEvent("zhin:auth-required"))),i2}var T=2e3,E=15e3,D={qrcode:{icon:b(c,{className:"w-4 h-4"}),label:"扫码登录"},sms:{icon:b(o,{className:"w-4 h-4"}),label:"短信验证码"},device:{icon:b(u,{className:"w-4 h-4"}),label:"设备验证"},slider:{icon:b(s,{className:"w-4 h-4"}),label:"滑块验证"}};function O(){let[e2,t2]=v([]),[r2,o2]=v(true),[s2,c2]=v(null),[l2,u2]=v({}),[d2,f2]=v({}),p2=_(0),y2=_(),S2=h(async()=>{try{let e3=await w("/api/login-assist/pending");if(!e3.ok)throw Error("获取待办失败");let n2=await e3.json(),r3=(Array.isArray(n2)?n2:[]).filter(e4=>e4.adapter==="icqq");t2(r3),c2(null),p2.current=r3.length>0?0:p2.current+1}catch(e3){c2(e3.message),p2.current++}finally{o2(false)}},[]),C2=h(()=>{let e3=Math.min(T*1.5**p2.current,E);y2.current=setTimeout(async()=>{await S2(),C2()},e3)},[S2]);g(()=>(S2().then(C2),()=>clearTimeout(y2.current)),[S2,C2]);let O2=async(e3,t3)=>{u2(t4=>({...t4,[e3]:true}));try{if(!(await w("/api/login-assist/submit",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e3,value:t3})})).ok)throw Error("提交失败");f2(t4=>{let n2={...t4};return delete n2[e3],n2}),p2.current=0,await S2()}catch(e4){c2(e4.message)}finally{u2(t4=>({...t4,[e3]:false}))}},k2=async e3=>{try{(await w("/api/login-assist/cancel",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:e3})})).ok&&await S2()}catch{}};return r2&&e2.length===0?b("div",{className:"flex justify-center py-12",children:b(i,{className:"w-6 h-6 animate-spin text-blue-500"})}):x("div",{className:"space-y-4",children:[b("p",{className:"text-sm text-muted-foreground",children:"扫码、短信或滑块等验证会出现在下方;刷新页面后仍可继续处理(仅 ICQQ 待办)。"}),s2&&x("div",{className:"flex items-center gap-2 p-3 rounded border border-red-200 bg-red-50 text-red-600 text-sm dark:bg-red-900/20 dark:text-red-400 dark:border-red-800",children:[b(n,{className:"w-4 h-4 shrink-0"}),b("span",{children:s2}),b("button",{onClick:()=>c2(null),className:"ml-auto",children:b(m,{className:"w-3.5 h-3.5"})})]}),e2.length===0?x("div",{className:"border rounded-lg bg-card p-12 text-center",children:[b(a,{className:"w-16 h-16 mx-auto mb-4 opacity-25"}),b("h3",{className:"text-lg font-semibold mb-1",children:"暂无待办"}),b("p",{className:"text-sm text-muted-foreground",children:"机器人需要验证时,待办将显示在此处"})]}):b("div",{className:"space-y-4",children:e2.map(e3=>{let t3=D[e3.type]??{icon:b(a,{className:"w-4 h-4"}),label:e3.type};return x("div",{className:"border rounded-lg bg-card p-4 shadow-sm",children:[x("div",{className:"flex items-start justify-between flex-wrap gap-2 mb-2",children:[x("div",{className:"flex items-center gap-2 font-semibold",children:[t3.icon," ",t3.label]}),x("div",{className:"flex gap-1 text-xs",children:[b("span",{className:"px-2 py-0.5 rounded border",children:e3.adapter}),b("span",{className:"px-2 py-0.5 rounded bg-muted",children:e3.botId})]})]}),e3.payload?.message&&b("p",{className:"text-sm text-muted-foreground mb-3",children:e3.payload.message}),e3.type==="qrcode"&&e3.payload?.image&&b("div",{className:"flex justify-center p-4 bg-muted/30 rounded-lg mb-3",children:b("img",{src:String(e3.payload.image),alt:"登录二维码",className:"max-w-[200px] w-full h-auto"})}),e3.type==="slider"&&e3.payload?.url&&x("p",{className:"text-sm break-all mb-3",children:[b("span",{className:"text-muted-foreground",children:"滑块链接:"})," ",b("a",{href:String(e3.payload.url),target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:String(e3.payload.url)})]}),(e3.type==="sms"||e3.type==="device"||e3.type==="slider")&&x("div",{className:"flex flex-wrap gap-2 items-center",children:[b("input",{className:"h-9 px-3 rounded-md border border-input bg-background text-sm max-w-[18rem] flex-1",placeholder:e3.type==="slider"?"输入 ticket":"输入验证码",value:d2[e3.id]??"",onChange:t4=>f2(n2=>({...n2,[e3.id]:t4.target.value})),onKeyDown:t4=>{if(t4.key==="Enter"){let t5=d2[e3.id]?.trim();t5&&O2(e3.id,e3.type==="slider"?{ticket:t5}:t5)}}}),b("button",{className:"h-9 px-4 rounded-md bg-primary text-primary-foreground text-sm disabled:opacity-50",disabled:l2[e3.id]||!d2[e3.id]?.trim(),onClick:()=>{let t4=d2[e3.id]?.trim();t4&&O2(e3.id,e3.type==="slider"?{ticket:t4}:t4)},children:l2[e3.id]?"提交中…":"提交"})]}),e3.type==="qrcode"&&x("div",{className:"flex gap-2 mt-3",children:[b("button",{className:"h-9 px-4 rounded-md bg-primary text-primary-foreground text-sm disabled:opacity-50",disabled:!!l2[e3.id],onClick:()=>void O2(e3.id,{done:true}),children:l2[e3.id]?"提交中…":"我已扫码"}),b("button",{className:"h-9 px-4 rounded-md border text-sm hover:bg-accent transition-colors",onClick:()=>void k2(e3.id),children:"取消"})]})]},e3.id)})})]})}function k(){let[e2,n2]=v("overview"),[o2,s2]=v([]),[c2,u2]=v(true),[m2,_2]=v(null),S2=h(async()=>{u2(true),_2(null);try{let e3=await w("/api/icqq/bots"),t2=await e3.json();if(!e3.ok||!t2.success)throw Error(t2.message||"加载失败");s2(Array.isArray(t2.data)?t2.data:[])}catch(e3){_2(e3.message),s2([])}finally{u2(false)}},[]);return g(()=>{e2==="overview"&&S2()},[e2,S2]),x("div",{className:"p-6 max-w-5xl mx-auto space-y-5",children:[x("div",{className:"flex items-center justify-between",children:[x("div",{children:[x("h1",{className:"text-2xl font-bold flex items-center gap-2",children:[b(r,{className:"w-6 h-6"})," ICQQ 管理"]}),b("p",{className:"text-sm text-muted-foreground mt-1",children:"机器人概览与登录辅助(扫码 / 验证码 / 滑块)"})]}),e2==="overview"&&x("button",{onClick:S2,disabled:c2,className:"flex items-center gap-1 px-3 py-1.5 rounded bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50 text-sm",children:[b(l,{className:`w-4 h-4 ${c2?"animate-spin":""}`})," 刷新"]})]}),x("div",{className:"flex gap-1 border-b",children:[b("button",{onClick:()=>n2("overview"),className:`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${e2==="overview"?"border-blue-500 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:x("span",{className:"flex items-center gap-1.5",children:[b(r,{className:"w-4 h-4"})," 概览"]})}),b("button",{onClick:()=>n2("login"),className:`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${e2==="login"?"border-blue-500 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:x("span",{className:"flex items-center gap-1.5",children:[b(a,{className:"w-4 h-4"})," 登录辅助"]})})]}),e2==="overview"&&x(y,{children:[m2&&b("div",{className:"p-3 bg-red-50 text-red-600 rounded border border-red-200 text-sm dark:bg-red-900/20 dark:text-red-400 dark:border-red-800",children:m2}),c2?b("div",{className:"flex justify-center py-12",children:b(i,{className:"w-6 h-6 animate-spin text-blue-500"})}):o2.length===0?b("div",{className:"text-center text-muted-foreground py-12",children:"暂无 ICQQ 机器人实例"}):b("div",{className:"grid gap-4 md:grid-cols-2",children:o2.map(e3=>x("div",{className:"border rounded-lg p-4 bg-card shadow-sm",children:[x("div",{className:"flex items-center justify-between mb-3",children:[b("span",{className:"font-medium text-lg",children:e3.name}),e3.connected?x("span",{className:"flex items-center gap-1 text-green-600 text-sm",children:[b(f,{className:"w-4 h-4"})," 在线"]}):x("span",{className:"flex items-center gap-1 text-gray-400 text-sm",children:[b(p,{className:"w-4 h-4"})," 离线"]})]}),x("div",{className:"space-y-2 text-sm text-muted-foreground",children:[x("div",{className:"flex items-center gap-1.5",children:[b(d,{className:"w-3.5 h-3.5"}),"群 ",e3.groupCount," · 好友 ",e3.friendCount]}),x("div",{className:"flex items-center gap-1.5",children:[b(t,{className:"w-3.5 h-3.5"}),"登录方式 ",e3.loginMode," · ",e3.status]})]})]},e3.name))})]}),e2==="login"&&b(O,{})]})}e({key:"icqq-management",path:"/icqq",title:"ICQQ管理",icon:b(r,{className:"w-5 h-5"}),element:b(k,{})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-icqq",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Zhin.js adapter for ICQQ (QQ Bot)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -52,15 +52,15 @@
52
52
  "lucide-react": "^0.469.0",
53
53
  "radix-ui": "^1.4.3",
54
54
  "typescript": "^5.9.3",
55
- "@zhin.js/console": "2.0.3",
56
- "zhin.js": "1.0.61"
55
+ "@zhin.js/console": "2.0.5",
56
+ "zhin.js": "1.0.63"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@zhin.js/client": "1.0.15",
60
- "@zhin.js/console": "2.0.3",
61
- "@zhin.js/core": "1.1.3",
62
- "@zhin.js/http": "1.0.56",
63
- "zhin.js": "1.0.61"
60
+ "@zhin.js/console": "2.0.5",
61
+ "@zhin.js/core": "1.1.5",
62
+ "zhin.js": "1.0.63",
63
+ "@zhin.js/http": "1.0.58"
64
64
  },
65
65
  "peerDependenciesMeta": {
66
66
  "@zhin.js/client": {
@@ -26,6 +26,9 @@ tools:
26
26
  - icqq_poke
27
27
  - icqq_list_muted
28
28
  - icqq_send_user_like
29
+ - icqq_set_anonymous
30
+ - icqq_group_files
31
+ - icqq_friend_list
29
32
  # 通用群管工具
30
33
  - icqq_kick_member
31
34
  - icqq_mute_member
@@ -47,6 +50,9 @@ tools:
47
50
  | `icqq_poke` | 戳一戳互动 | user |
48
51
  | `icqq_list_muted` | 查询禁言列表 | user |
49
52
  | `icqq_send_user_like` | 给用户点赞 | user |
53
+ | `icqq_set_anonymous` | 开启/关闭匿名聊天 | group_admin |
54
+ | `icqq_group_files` | 获取群文件列表 | user |
55
+ | `icqq_friend_list` | 获取好友列表 | user |
50
56
 
51
57
  ### 通用群管
52
58