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绿
90 lines
3.3 KiB
Go
90 lines
3.3 KiB
Go
package kook
|
|
|
|
// This file adds the "token 即用、其余自动获取" bot-identity lookup. After the
|
|
// adapter holds a token, Connect calls GET /api/v3/user/me to resolve the bot
|
|
// account (id/username/nickname) so the admin channel list can show who the bot
|
|
// is without the user hand-configuring any non-token field. The lookup is
|
|
// best-effort: a bad token or a transient network failure logs a warning and
|
|
// leaves the bot identity empty, but the adapter still establishes its session
|
|
// and routes messages normally. The identity is surfaced through Config() under
|
|
// the non-sensitive bot_id/bot_username/bot_nickname keys, so it reaches the
|
|
// admin read path without ever persisting or logging the token.
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// userMePath is the KOOK REST endpoint that returns the bot account for a bearer
|
|
// token.
|
|
const userMePath = "/user/me"
|
|
|
|
// botIdentity is the "data" payload of GET /api/v3/user/me. Only the three
|
|
// display-relevant fields are captured; the rest of the payload is dropped.
|
|
type botIdentity struct {
|
|
ID string `json:"id"`
|
|
Username string `json:"username"`
|
|
Nickname string `json:"nickname"`
|
|
}
|
|
|
|
// fetchBotIdentity calls GET /api/v3/user/me with the bearer token and decodes
|
|
// the bot account. An empty id is treated as a bad response, so a caller can
|
|
// distinguish "no account" from "no identity yet".
|
|
func (a *Adapter) fetchBotIdentity(ctx context.Context) (botIdentity, error) {
|
|
data, err := a.http.GetJSON(ctx, userMePath, nil)
|
|
if err != nil {
|
|
return botIdentity{}, fmt.Errorf("kook: fetch bot identity: %w", err)
|
|
}
|
|
var bi botIdentity
|
|
if err := json.Unmarshal(data, &bi); err != nil {
|
|
return botIdentity{}, fmt.Errorf("kook: decode bot identity: %w", err)
|
|
}
|
|
if bi.ID == "" {
|
|
return botIdentity{}, fmt.Errorf("kook: bot identity empty: %w", ErrBadResponse)
|
|
}
|
|
return bi, nil
|
|
}
|
|
|
|
// refreshBotIdentity resolves the bot account (best-effort) and stores it on the
|
|
// adapter. A failure is logged and ignored so a bad token or network hiccup never
|
|
// aborts the connect; the identity is simply left empty. Only the bot's public
|
|
// handle/display name is logged — never the token or the user id.
|
|
func (a *Adapter) refreshBotIdentity(ctx context.Context) {
|
|
bi, err := a.fetchBotIdentity(ctx)
|
|
if err != nil {
|
|
a.log.Warn("bot identity lookup failed", "err", err)
|
|
return
|
|
}
|
|
a.setBotIdentity(bi)
|
|
if bi.Nickname != "" {
|
|
a.log.Info("connected as bot", "nickname", bi.Nickname)
|
|
} else if bi.Username != "" {
|
|
a.log.Info("connected as bot", "username", bi.Username)
|
|
}
|
|
}
|
|
|
|
// setBotIdentity stores the resolved bot account under a dedicated mutex.
|
|
func (a *Adapter) setBotIdentity(bi botIdentity) {
|
|
a.botMu.Lock()
|
|
defer a.botMu.Unlock()
|
|
a.botID = bi.ID
|
|
a.botUsername = bi.Username
|
|
a.botNickname = bi.Nickname
|
|
}
|
|
|
|
// botIdentitySnapshot returns the last resolved bot account (empty until a
|
|
// successful Connect + user/me lookup).
|
|
func (a *Adapter) botIdentitySnapshot() (id, username, nickname string) {
|
|
a.botMu.RLock()
|
|
defer a.botMu.RUnlock()
|
|
return a.botID, a.botUsername, a.botNickname
|
|
}
|
|
|
|
// BotIdentity returns the resolved bot account (id/username/nickname). The values
|
|
// are non-sensitive (the bot's public handle and display name) so the admin panel
|
|
// may show them.
|
|
func (a *Adapter) BotIdentity() (id, username, nickname string) {
|
|
return a.botIdentitySnapshot()
|
|
}
|