This commit is contained in:
lpf
2026-02-19 10:00:59 +08:00
parent 27c718def9
commit 1f38852f49
14 changed files with 201 additions and 148 deletions

View File

@@ -82,7 +82,7 @@ func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []st
return
}
// 生成 SessionKey: channel:chatID
// Build session key: channel:chatID
sessionKey := fmt.Sprintf("%s:%s", c.name, chatID)
msg := bus.InboundMessage{

View File

@@ -15,7 +15,7 @@ import (
"github.com/open-dingtalk/dingtalk-stream-sdk-go/client"
)
// DingTalkChannel implements the Channel interface for DingTalk (钉钉)
// DingTalkChannel implements the Channel interface for DingTalk.
// It uses WebSocket for receiving messages via stream mode and API for sending
type DingTalkChannel struct {
*BaseChannel

View File

@@ -49,32 +49,32 @@ func (c *QQChannel) Start(ctx context.Context) error {
logger.InfoC("qq", "Starting QQ bot (WebSocket mode)")
// 创建 token source
// Create token source
credentials := &token.QQBotCredentials{
AppID: c.config.AppID,
AppSecret: c.config.AppSecret,
}
c.tokenSource = token.NewQQBotTokenSource(credentials)
// 创建子 context
// Create child context
runCtx, cancel := context.WithCancel(ctx)
c.runCancel.set(cancel)
// 启动自动刷新 token 协程
// Start token auto-refresh goroutine
if err := token.StartRefreshAccessToken(runCtx, c.tokenSource); err != nil {
return fmt.Errorf("failed to start token refresh: %w", err)
}
// 初始化 OpenAPI 客户端
// Initialize OpenAPI client
c.api = botgo.NewOpenAPI(c.config.AppID, c.tokenSource).WithTimeout(5 * time.Second)
// 注册事件处理器
// Register event handlers
intent := event.RegisterHandlers(
c.handleC2CMessage(),
c.handleGroupATMessage(),
)
// 获取 WebSocket 接入点
// Get WebSocket endpoint
wsInfo, err := c.api.WS(runCtx, nil, "")
if err != nil {
return fmt.Errorf("failed to get websocket info: %w", err)
@@ -84,10 +84,10 @@ func (c *QQChannel) Start(ctx context.Context) error {
"shards": wsInfo.Shards,
})
// 创建并保存 sessionManager
// Create and store session manager
c.sessionManager = botgo.NewSessionManager()
// 在 goroutine 中启动 WebSocket 连接,避免阻塞
// Start WebSocket connection in a goroutine to avoid blocking
runChannelTask("qq", "websocket session", func() error {
return c.sessionManager.Start(wsInfo, c.tokenSource, &intent)
}, func(_ error) {
@@ -116,12 +116,12 @@ func (c *QQChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
return fmt.Errorf("QQ bot not running")
}
// 构造消息
// Build message
msgToCreate := &dto.MessageToCreate{
Content: msg.Content,
}
// C2C 消息发送
// Send C2C message
_, err := c.api.PostC2CMessage(ctx, msg.ChatID, msgToCreate)
if err != nil {
logger.ErrorCF("qq", "Failed to send C2C message", map[string]interface{}{
@@ -133,15 +133,15 @@ func (c *QQChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
return nil
}
// handleC2CMessage 处理 QQ 私聊消息
// handleC2CMessage handles QQ private messages
func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
return func(event *dto.WSPayload, data *dto.WSC2CMessageData) error {
// 去重检查
// Deduplication check
if c.isDuplicate(data.ID) {
return nil
}
// 提取用户信息
// Extract sender information
var senderID string
if data.Author != nil && data.Author.ID != "" {
senderID = data.Author.ID
@@ -150,7 +150,7 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
return nil
}
// 提取消息内容
// Extract message content
content := data.Content
if content == "" {
logger.DebugC("qq", "Received empty message, ignoring")
@@ -163,7 +163,7 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
logger.FieldMessageContentLength: len(content),
})
// 转发到消息总线
// Forward to message bus
metadata := map[string]string{
"message_id": data.ID,
}
@@ -174,15 +174,15 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
}
}
// handleGroupATMessage 处理群@消息
// handleGroupATMessage handles group @ messages
func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
return func(event *dto.WSPayload, data *dto.WSGroupATMessageData) error {
// 去重检查
// Deduplication check
if c.isDuplicate(data.ID) {
return nil
}
// 提取用户信息
// Extract sender information
var senderID string
if data.Author != nil && data.Author.ID != "" {
senderID = data.Author.ID
@@ -191,7 +191,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
return nil
}
// 提取消息内容(去掉 @ 机器人部分)
// Extract message content (remove bot mention)
content := data.Content
if content == "" {
logger.DebugC("qq", "Received empty group message, ignoring")
@@ -204,7 +204,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
logger.FieldMessageContentLength: len(content),
})
// 转发到消息总线(使用 GroupID 作为 ChatID
// Forward to message bus (use GroupID as ChatID)
metadata := map[string]string{
"message_id": data.ID,
"group_id": data.GroupID,
@@ -216,7 +216,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
}
}
// isDuplicate 检查消息是否重复
// isDuplicate checks whether a message is duplicated
func (c *QQChannel) isDuplicate(messageID string) bool {
c.mu.Lock()
defer c.mu.Unlock()
@@ -227,9 +227,9 @@ func (c *QQChannel) isDuplicate(messageID string) bool {
c.processedIDs[messageID] = true
// 简单清理:限制 map 大小
// Simple cleanup: limit map size
if len(c.processedIDs) > 10000 {
// 清空一半
// Remove half of the entries
count := 0
for id := range c.processedIDs {
if count >= 5000 {