feat: KOOK自动获取bot身份 + action/narration剥出气泡 + 渠道配置表单回填
agent-116 KOOK token即用+自动获取:
- kook/identity.go: Connect 调 GET /user/me 拿 bot 身份(bot_id/bot_username/bot_nickname), 失败容错
- 渠道列表 enrichAccountsWithBot 附 bot 身份; base_url空用默认/receive_mode默认ws(只填token可跑)
agent-117 action/narration 剥出气泡(主人需求: 动作/旁白不进气泡,按位置上/下):
- parseContent.splitBubble(content)->{body,head,tail}: body=纯文本(去标签), head=开头/中间action/narr(气泡上), tail=结尾(气泡下)
- renderContent 只渲染text(去内联灰斜体); chat.tsx BubbleNotes 泡外 *动作/旁白* 灰斜体小字(气泡外依附), SplitChatBubble 先剥splitBubble再splitParagraphs(body), <p>/\n\n/毛玻璃/SourceLabel不破坏
- 注: 非chat mode renderContent 现在丢弃action/narr(可接受)
Channels 配置表单回填(修save后输入框空):
- openConfig 从 data.accounts[0].config 回填 token(掩码提示)/base_url/平台字段/黑白名单/限流, 不再硬清空
全仓 build/vet/test绿+前端build绿
This commit is contained in:
1 parent
bdd9d752e1
commit
65091f6497
15 files changed
+571
-83
No files matched your search
@@ -34,12 +34,13 @@ interface PlatformField {
|
||||
* are rendered for every platform separately; this map holds only what a given
|
||||
* adapter additionally needs. Keys are the adapter Config json tags verbatim. */
|
||||
const PLATFORM_FIELDS: Record<string, PlatformField[]> = {
|
||||
kook: [
|
||||
{ key: "receive_mode", label: "receive_mode(收包模式 ws/webhook)", placeholder: "ws" },
|
||||
{ key: "webhook_path", label: "webhook_path(webhook 挂载路由)", placeholder: "/webhook/kook" },
|
||||
{ key: "webhook_verify_token", label: "webhook_verify_token(校验 token)", secret: true },
|
||||
{ key: "webhook_encrypt_key", label: "webhook_encrypt_key(AES 加密密钥)", secret: true },
|
||||
],
|
||||
// KOOK is "token 即用、其余自动获取": only the token is required. base_url
|
||||
// defaults to the official root, receive_mode defaults to "ws" and webhook_path
|
||||
// to "/webhook/kook", so none of them need a form field. Advanced values
|
||||
// (receive_mode="webhook", webhook_path, webhook_verify_token, webhook_encrypt_key)
|
||||
// are set through the "额外 config" JSON instead. We keep the (empty) entry so
|
||||
// the panel still renders the KOOK-specific hint below.
|
||||
kook: [],
|
||||
"qq-obv11": [
|
||||
{ key: "mode", label: "mode(client/server)", placeholder: "server" },
|
||||
{ key: "listen_addr", label: "listen_addr(server 监听)", placeholder: "0.0.0.0:6700" },
|
||||
@@ -132,18 +133,29 @@ export function ChannelsModule(): React.ReactNode {
|
||||
const openConfig = (name: string) => {
|
||||
setErr(null);
|
||||
setNote(null);
|
||||
setCfgPlatform(platformOf(name));
|
||||
setCfgId("");
|
||||
setToken("");
|
||||
setBaseURL("");
|
||||
setPlatVals({});
|
||||
setAllowGroups("");
|
||||
setDenyGroups("");
|
||||
setAllowUsers("");
|
||||
setDenyUsers("");
|
||||
setGroupMsgPerMin("");
|
||||
setMsgPerMin("");
|
||||
setMaxMessages("");
|
||||
const platform = platformOf(name);
|
||||
setCfgPlatform(platform);
|
||||
// Prefill from the backend-provided account (if already configured) so a
|
||||
// saved config is visible again instead of the form opening blank. token is
|
||||
// masked by the backend (never raw), so we only echo it as a hint.
|
||||
const acct = (data ?? []).find((c) => platformOf(c.name) === platform)?.accounts?.[0];
|
||||
setCfgId(acct?.id ?? "");
|
||||
const cfg = acct?.config ?? {};
|
||||
setToken(typeof cfg.bot_token === "string" && cfg.bot_token ? "(已配置,仅掩码显示)" : "");
|
||||
setBaseURL(typeof cfg.base_url === "string" ? cfg.base_url : "");
|
||||
const plat: Record<string, string> = {};
|
||||
for (const f of PLATFORM_FIELDS[platform] ?? []) {
|
||||
const v = cfg[f.key];
|
||||
plat[f.key] = v == null ? "" : String(v);
|
||||
}
|
||||
setPlatVals(plat);
|
||||
setAllowGroups((acct?.allowGroups ?? []).join(","));
|
||||
setDenyGroups((acct?.denyGroups ?? []).join(","));
|
||||
setAllowUsers((acct?.allowUsers ?? []).join(","));
|
||||
setDenyUsers(acct?.denyUsers ? (Array.isArray(acct.denyUsers) ? acct.denyUsers.join(",") : "") : "");
|
||||
setGroupMsgPerMin(cfg.groupMsgPerMin == null ? "" : String(cfg.groupMsgPerMin));
|
||||
setMsgPerMin(cfg.msgPerMin == null ? "" : String(cfg.msgPerMin));
|
||||
setMaxMessages(cfg.maxMessages == null ? "" : String(cfg.maxMessages));
|
||||
setExtraConfig("");
|
||||
};
|
||||
|
||||
@@ -253,16 +265,21 @@ export function ChannelsModule(): React.ReactNode {
|
||||
render: (c) =>
|
||||
c.accounts && c.accounts.length ? (
|
||||
<span className="flex flex-wrap items-center gap-1">
|
||||
{c.accounts.map((a) => (
|
||||
<span key={a.id || "default"} className="flex items-center gap-1">
|
||||
<Tag tone="neutral">{a.id || "default"}</Tag>
|
||||
{a.id ? (
|
||||
<Button size="sm" variant="ghost" disabled={busy !== null} onClick={() => del(platformOf(c.name), a.id)}>
|
||||
删除
|
||||
</Button>
|
||||
) : null}
|
||||
</span>
|
||||
))}
|
||||
{c.accounts.map((a) => {
|
||||
// Show the connected bot's resolved identity (kook user/me) when the
|
||||
// adapter fetched it, falling back to the account id.
|
||||
const label = a.botNickname || (a.botUsername ? `@${a.botUsername}` : a.id || "default");
|
||||
return (
|
||||
<span key={a.id || "default"} className="flex items-center gap-1">
|
||||
<Tag tone="neutral">{label}</Tag>
|
||||
{a.id ? (
|
||||
<Button size="sm" variant="ghost" disabled={busy !== null} onClick={() => del(platformOf(c.name), a.id)}>
|
||||
删除
|
||||
</Button>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-xs text-[var(--mu-text-lo)]">—</span>
|
||||
@@ -316,6 +333,14 @@ export function ChannelsModule(): React.ReactNode {
|
||||
{cfgPlatform ? (
|
||||
<Surface className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">配置账号 → {cfgPlatform}</h3>
|
||||
{cfgPlatform === "kook" ? (
|
||||
<p className="text-xs text-[var(--mu-text-lo)]">
|
||||
KOOK 为「token 即用、其余自动获取」:仅需填 token(必填);base_url 留空即用官方默认;receive_mode 默认 ws、webhook_path 默认 /webhook/kook,均无需填写。若改用 webhook 或配校验/加密,在下方「额外 config」JSON 里设置,如
|
||||
{" "}
|
||||
{"{"}"receive_mode":"webhook","webhook_path":"/webhook/kook","webhook_verify_token":"...","webhook_encrypt_key":"..."{"}"}
|
||||
(verify_token / encrypt_key 仅在 webhook 模式需要)。连接后渠道列表会显示该 KOOK bot 身份。
|
||||
</p>
|
||||
) : null}
|
||||
<p className="text-xs text-[var(--mu-text-lo)]">
|
||||
保存后写入 channels.json(本地、已 gitignore)并热更新 {cfgPlatform} 的渠道 adapter,无需重启进程。token 只在写文件时出现,列表/响应不回显裸值。
|
||||
</p>
|
||||
|
||||
Reference in new issue
Block a user