refactor: QQ → OBv11 重命名 + 平台格式统一抽象
- 所有对外称呼从 QQ 改为 OBv11(注释/提示词/日志/配置项) - 新增 PlatformFormat 结构体,统一管理平台消息标记格式 - defaultPlatformFormats() 注册表替代硬编码 qqTargetRe - extractProactiveMessage 改为 Thinker 方法,遍历格式注册表匹配 - 配置项重命名: QQ_BOT_PORT → OBV11_BOT_PORT, QQBotPort → OBv11BotPort - 标记格式: 【QQ群聊】→【OBv11群聊】、【QQ私聊】→【OBv11私聊】 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -298,16 +298,16 @@ func main() {
|
||||
}
|
||||
// Sync admin identities from config fields.
|
||||
syncAdminUIDs(mapper, platform, fields, cfg.AdminNickname)
|
||||
// Restart QQ reader when QQ config changes.
|
||||
// Restart OBv11 reader when OBv11 config changes.
|
||||
if platform == "qq" {
|
||||
startQQReaders(router)
|
||||
startOBv11Readers(router)
|
||||
}
|
||||
} else {
|
||||
router.RemoveAdapter(name)
|
||||
fmt.Printf("Platform adapter removed: %s\n", name)
|
||||
// Cancel reader goroutines for removed adapter.
|
||||
if platform == "qq" {
|
||||
startQQReaders(router)
|
||||
startOBv11Readers(router)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -324,8 +324,8 @@ func main() {
|
||||
blh := handler.NewBlocklistHandler(blocklistStore)
|
||||
blh.RegisterRoutes(mux)
|
||||
|
||||
// Start QQ message reader loop.
|
||||
startQQReaders(router)
|
||||
// Start OBv11 message reader loop.
|
||||
startOBv11Readers(router)
|
||||
|
||||
addr := ":" + cfg.Port
|
||||
srv := &http.Server{Addr: addr, Handler: mux}
|
||||
@@ -355,8 +355,8 @@ func main() {
|
||||
var qqReaderCancels = make(map[string]context.CancelFunc)
|
||||
var qqReaderCancelsMu sync.Mutex
|
||||
|
||||
// startQQReaders cancels any existing QQ readers and starts one per registered QQ adapter.
|
||||
func startQQReaders(router *bridge.PlatformRouter) {
|
||||
// startOBv11Readers cancels any existing OBv11 readers and starts one per registered OBv11 adapter.
|
||||
func startOBv11Readers(router *bridge.PlatformRouter) {
|
||||
// Cancel all existing readers.
|
||||
qqReaderCancelsMu.Lock()
|
||||
for _, cancel := range qqReaderCancels {
|
||||
@@ -392,7 +392,7 @@ func startQQReaders(router *bridge.PlatformRouter) {
|
||||
// Dispatcher: route to worker by session hash so same conversation stays ordered.
|
||||
go func() {
|
||||
for msg := range rawCh {
|
||||
idx := hashQQSession(msg) % numWorkers
|
||||
idx := hashOBv11Session(msg) % numWorkers
|
||||
workerChs[idx] <- msg
|
||||
}
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
@@ -503,7 +503,7 @@ func createAdapters(cfg *config.Config, store *config.Store) []bridge.PlatformAd
|
||||
func createSingleAdapter(cfg *config.Config, platform, configName string, fields map[string]string) bridge.PlatformAdapter {
|
||||
switch platform {
|
||||
case "qq":
|
||||
port := cfg.QQBotPort
|
||||
port := cfg.OBv11BotPort
|
||||
if p, ok := fields["bot_port"]; ok && p != "" {
|
||||
port = p
|
||||
}
|
||||
@@ -564,8 +564,8 @@ func mergeFields(cfg *config.Config, platform string, stored *config.PlatformCon
|
||||
if fields["webhook_url"] == "" && cfg.TelegramWebhookURL != "" && platform == "telegram" {
|
||||
fields["webhook_url"] = cfg.TelegramWebhookURL
|
||||
}
|
||||
if fields["bot_port"] == "" && cfg.QQBotPort != "" && platform == "qq" {
|
||||
fields["bot_port"] = cfg.QQBotPort
|
||||
if fields["bot_port"] == "" && cfg.OBv11BotPort != "" && platform == "qq" {
|
||||
fields["bot_port"] = cfg.OBv11BotPort
|
||||
}
|
||||
return fields
|
||||
}
|
||||
@@ -928,10 +928,10 @@ func hasOnlySilentMessages(messages []bridge.ResponseMessage) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// hashQQSession returns a hash for dispatching a QQ message to a worker.
|
||||
// hashOBv11Session returns a hash for dispatching an OBv11 message to a worker.
|
||||
// Messages from the same conversation (private or group) get the same hash,
|
||||
// preserving ordering within a session while allowing cross-session parallelism.
|
||||
func hashQQSession(msg *qqadapter.OBv11Message) uint32 {
|
||||
func hashOBv11Session(msg *qqadapter.OBv11Message) uint32 {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(msg.MessageType))
|
||||
h.Write([]byte(":"))
|
||||
@@ -966,7 +966,7 @@ func parseIntOr(s string, defaultVal int) int {
|
||||
func seedIdentities(m *bridge.IdentityMapper, store *config.Store, adminNickname string) {
|
||||
// From environment variables.
|
||||
for _, entry := range []struct{ envKey, platform string }{
|
||||
{"QQ_ADMIN_UID", "qq"},
|
||||
{"OBV11_ADMIN_UID", "obv11"},
|
||||
{"TELEGRAM_ADMIN_UID", "telegram"},
|
||||
} {
|
||||
if raw := os.Getenv(entry.envKey); raw != "" {
|
||||
|
||||
@@ -21,7 +21,7 @@ type PlatformAdapter interface {
|
||||
}
|
||||
|
||||
// ProactiveSender is an optional interface for adapters that can
|
||||
// proactively send messages (e.g., QQ bot sending without prior request).
|
||||
// proactively send messages (e.g., OBv11 bot sending without prior request).
|
||||
type ProactiveSender interface {
|
||||
SendProactive(chatType string, userID, groupID int64, content string) error
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ type Config struct {
|
||||
InternalToken string
|
||||
|
||||
// Platform-specific.
|
||||
QQBotPort string // port for QQ OBv11 reverse WebSocket
|
||||
OBv11BotPort string // port for OBv11 reverse WebSocket
|
||||
TelegramToken string // Telegram Bot API token
|
||||
TelegramWebhookURL string // public webhook URL for Telegram
|
||||
|
||||
@@ -34,7 +34,7 @@ func Load() *Config {
|
||||
Env: "development",
|
||||
GatewayURL: "http://localhost:8080",
|
||||
AICoreURL: "http://localhost:8081",
|
||||
QQBotPort: "8096",
|
||||
OBv11BotPort: "8096",
|
||||
}
|
||||
if v := os.Getenv("PORT"); v != "" {
|
||||
cfg.Port = v
|
||||
@@ -51,8 +51,8 @@ func Load() *Config {
|
||||
if v := os.Getenv("INTERNAL_SERVICE_TOKEN"); v != "" {
|
||||
cfg.InternalToken = v
|
||||
}
|
||||
if v := os.Getenv("QQ_BOT_PORT"); v != "" {
|
||||
cfg.QQBotPort = v
|
||||
if v := os.Getenv("OBV11_BOT_PORT"); v != "" {
|
||||
cfg.OBv11BotPort = v
|
||||
}
|
||||
if v := os.Getenv("TELEGRAM_BOT_TOKEN"); v != "" {
|
||||
cfg.TelegramToken = v
|
||||
|
||||
@@ -180,7 +180,7 @@ func (h *BridgeHandler) sendProactive(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Map chat type to QQ message_type
|
||||
// Map chat type to OBv11 message_type
|
||||
msgType := req.ChatType
|
||||
if msgType != "private" && msgType != "group" {
|
||||
writeJSON(w, http.StatusBadRequest, errResp("chat_type must be private or group"))
|
||||
@@ -196,7 +196,7 @@ func (h *BridgeHandler) sendProactive(w http.ResponseWriter, r *http.Request) {
|
||||
content = fmt.Sprintf("[CQ:at,qq=%s] %s", req.AtUserID, content)
|
||||
}
|
||||
|
||||
// Find the adapter. For QQ, use "qq" as the default adapter name.
|
||||
// Find the adapter. For OBv11, use the platform name as adapter name.
|
||||
adapterName := req.Platform
|
||||
err := h.router.SendProactive(adapterName, msgType, userID, groupID, content)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user