Merge pull request #697 from xiaket/doc-remove-chinese-comments
[doc]translate Chinese comments
This commit is contained in:
commit
7cbfa89a96
5 changed files with 36 additions and 36 deletions
|
|
@ -131,7 +131,7 @@ func main() {
|
||||||
|
|
||||||
workspace := cfg.WorkspacePath()
|
workspace := cfg.WorkspacePath()
|
||||||
installer := skills.NewSkillInstaller(workspace)
|
installer := skills.NewSkillInstaller(workspace)
|
||||||
// 获取全局配置目录和内置 skills 目录
|
// get global config directory and builtin skills directory
|
||||||
globalDir := filepath.Dir(getConfigPath())
|
globalDir := filepath.Dir(getConfigPath())
|
||||||
globalSkillsDir := filepath.Join(globalDir, "skills")
|
globalSkillsDir := filepath.Join(globalDir, "skills")
|
||||||
builtinSkillsDir := filepath.Join(globalDir, "picoclaw", "skills")
|
builtinSkillsDir := filepath.Join(globalDir, "picoclaw", "skills")
|
||||||
|
|
|
||||||
|
|
@ -47,31 +47,31 @@ func (c *QQChannel) Start(ctx context.Context) error {
|
||||||
|
|
||||||
logger.InfoC("qq", "Starting QQ bot (WebSocket mode)")
|
logger.InfoC("qq", "Starting QQ bot (WebSocket mode)")
|
||||||
|
|
||||||
// 创建 token source
|
// create token source
|
||||||
credentials := &token.QQBotCredentials{
|
credentials := &token.QQBotCredentials{
|
||||||
AppID: c.config.AppID,
|
AppID: c.config.AppID,
|
||||||
AppSecret: c.config.AppSecret,
|
AppSecret: c.config.AppSecret,
|
||||||
}
|
}
|
||||||
c.tokenSource = token.NewQQBotTokenSource(credentials)
|
c.tokenSource = token.NewQQBotTokenSource(credentials)
|
||||||
|
|
||||||
// 创建子 context
|
// create child context
|
||||||
c.ctx, c.cancel = context.WithCancel(ctx)
|
c.ctx, c.cancel = context.WithCancel(ctx)
|
||||||
|
|
||||||
// 启动自动刷新 token 协程
|
// start auto-refresh token goroutine
|
||||||
if err := token.StartRefreshAccessToken(c.ctx, c.tokenSource); err != nil {
|
if err := token.StartRefreshAccessToken(c.ctx, c.tokenSource); err != nil {
|
||||||
return fmt.Errorf("failed to start token refresh: %w", err)
|
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)
|
c.api = botgo.NewOpenAPI(c.config.AppID, c.tokenSource).WithTimeout(5 * time.Second)
|
||||||
|
|
||||||
// 注册事件处理器
|
// register event handlers
|
||||||
intent := event.RegisterHandlers(
|
intent := event.RegisterHandlers(
|
||||||
c.handleC2CMessage(),
|
c.handleC2CMessage(),
|
||||||
c.handleGroupATMessage(),
|
c.handleGroupATMessage(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// 获取 WebSocket 接入点
|
// get WebSocket endpoint
|
||||||
wsInfo, err := c.api.WS(c.ctx, nil, "")
|
wsInfo, err := c.api.WS(c.ctx, nil, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get websocket info: %w", err)
|
return fmt.Errorf("failed to get websocket info: %w", err)
|
||||||
|
|
@ -81,10 +81,10 @@ func (c *QQChannel) Start(ctx context.Context) error {
|
||||||
"shards": wsInfo.Shards,
|
"shards": wsInfo.Shards,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 创建并保存 sessionManager
|
// create and save sessionManager
|
||||||
c.sessionManager = botgo.NewSessionManager()
|
c.sessionManager = botgo.NewSessionManager()
|
||||||
|
|
||||||
// 在 goroutine 中启动 WebSocket 连接,避免阻塞
|
// start WebSocket connection in goroutine to avoid blocking
|
||||||
go func() {
|
go func() {
|
||||||
if err := c.sessionManager.Start(wsInfo, c.tokenSource, &intent); err != nil {
|
if err := c.sessionManager.Start(wsInfo, c.tokenSource, &intent); err != nil {
|
||||||
logger.ErrorCF("qq", "WebSocket session error", map[string]any{
|
logger.ErrorCF("qq", "WebSocket session error", map[string]any{
|
||||||
|
|
@ -116,12 +116,12 @@ func (c *QQChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
||||||
return fmt.Errorf("QQ bot not running")
|
return fmt.Errorf("QQ bot not running")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构造消息
|
// construct message
|
||||||
msgToCreate := &dto.MessageToCreate{
|
msgToCreate := &dto.MessageToCreate{
|
||||||
Content: msg.Content,
|
Content: msg.Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
// C2C 消息发送
|
// send C2C message
|
||||||
_, err := c.api.PostC2CMessage(ctx, msg.ChatID, msgToCreate)
|
_, err := c.api.PostC2CMessage(ctx, msg.ChatID, msgToCreate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("qq", "Failed to send C2C message", map[string]any{
|
logger.ErrorCF("qq", "Failed to send C2C message", map[string]any{
|
||||||
|
|
@ -133,15 +133,15 @@ func (c *QQChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleC2CMessage 处理 QQ 私聊消息
|
// handleC2CMessage handles QQ private messages
|
||||||
func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
|
func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
|
||||||
return func(event *dto.WSPayload, data *dto.WSC2CMessageData) error {
|
return func(event *dto.WSPayload, data *dto.WSC2CMessageData) error {
|
||||||
// 去重检查
|
// deduplication check
|
||||||
if c.isDuplicate(data.ID) {
|
if c.isDuplicate(data.ID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取用户信息
|
// extract user info
|
||||||
var senderID string
|
var senderID string
|
||||||
if data.Author != nil && data.Author.ID != "" {
|
if data.Author != nil && data.Author.ID != "" {
|
||||||
senderID = data.Author.ID
|
senderID = data.Author.ID
|
||||||
|
|
@ -150,7 +150,7 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取消息内容
|
// extract message content
|
||||||
content := data.Content
|
content := data.Content
|
||||||
if content == "" {
|
if content == "" {
|
||||||
logger.DebugC("qq", "Received empty message, ignoring")
|
logger.DebugC("qq", "Received empty message, ignoring")
|
||||||
|
|
@ -162,7 +162,7 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
|
||||||
"length": len(content),
|
"length": len(content),
|
||||||
})
|
})
|
||||||
|
|
||||||
// 转发到消息总线
|
// forward to message bus
|
||||||
metadata := map[string]string{
|
metadata := map[string]string{
|
||||||
"message_id": data.ID,
|
"message_id": data.ID,
|
||||||
"peer_kind": "direct",
|
"peer_kind": "direct",
|
||||||
|
|
@ -175,15 +175,15 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleGroupATMessage 处理群@消息
|
// handleGroupATMessage handles group @messages
|
||||||
func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
|
func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
|
||||||
return func(event *dto.WSPayload, data *dto.WSGroupATMessageData) error {
|
return func(event *dto.WSPayload, data *dto.WSGroupATMessageData) error {
|
||||||
// 去重检查
|
// deduplication check
|
||||||
if c.isDuplicate(data.ID) {
|
if c.isDuplicate(data.ID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取用户信息
|
// extract user info
|
||||||
var senderID string
|
var senderID string
|
||||||
if data.Author != nil && data.Author.ID != "" {
|
if data.Author != nil && data.Author.ID != "" {
|
||||||
senderID = data.Author.ID
|
senderID = data.Author.ID
|
||||||
|
|
@ -192,7 +192,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取消息内容(去掉 @ 机器人部分)
|
// extract message content (remove @bot part)
|
||||||
content := data.Content
|
content := data.Content
|
||||||
if content == "" {
|
if content == "" {
|
||||||
logger.DebugC("qq", "Received empty group message, ignoring")
|
logger.DebugC("qq", "Received empty group message, ignoring")
|
||||||
|
|
@ -205,7 +205,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
|
||||||
"length": len(content),
|
"length": len(content),
|
||||||
})
|
})
|
||||||
|
|
||||||
// 转发到消息总线(使用 GroupID 作为 ChatID)
|
// forward to message bus (use GroupID as ChatID)
|
||||||
metadata := map[string]string{
|
metadata := map[string]string{
|
||||||
"message_id": data.ID,
|
"message_id": data.ID,
|
||||||
"group_id": data.GroupID,
|
"group_id": data.GroupID,
|
||||||
|
|
@ -219,7 +219,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isDuplicate 检查消息是否重复
|
// isDuplicate checks if message is duplicate
|
||||||
func (c *QQChannel) isDuplicate(messageID string) bool {
|
func (c *QQChannel) isDuplicate(messageID string) bool {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
@ -230,9 +230,9 @@ func (c *QQChannel) isDuplicate(messageID string) bool {
|
||||||
|
|
||||||
c.processedIDs[messageID] = true
|
c.processedIDs[messageID] = true
|
||||||
|
|
||||||
// 简单清理:限制 map 大小
|
// simple cleanup: limit map size
|
||||||
if len(c.processedIDs) > 10000 {
|
if len(c.processedIDs) > 10000 {
|
||||||
// 清空一半
|
// clear half
|
||||||
count := 0
|
count := 0
|
||||||
for id := range c.processedIDs {
|
for id := range c.processedIDs {
|
||||||
if count >= 5000 {
|
if count >= 5000 {
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查白名单,避免为被拒绝的用户下载附件
|
// check allowlist to avoid downloading attachments for rejected users
|
||||||
if !c.IsAllowed(ev.User) {
|
if !c.IsAllowed(ev.User) {
|
||||||
logger.DebugCF("slack", "Message rejected by allowlist", map[string]any{
|
logger.DebugCF("slack", "Message rejected by allowlist", map[string]any{
|
||||||
"user_id": ev.User,
|
"user_id": ev.User,
|
||||||
|
|
@ -232,9 +232,9 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
|
||||||
content = c.stripBotMention(content)
|
content = c.stripBotMention(content)
|
||||||
|
|
||||||
var mediaPaths []string
|
var mediaPaths []string
|
||||||
localFiles := []string{} // 跟踪需要清理的本地文件
|
localFiles := []string{} // track local files that need cleanup
|
||||||
|
|
||||||
// 确保临时文件在函数返回时被清理
|
// ensure temp files are cleaned up when function returns
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, file := range localFiles {
|
for _, file := range localFiles {
|
||||||
if err := os.Remove(file); err != nil {
|
if err := os.Remove(file); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes
|
||||||
senderID = fmt.Sprintf("%d|%s", user.ID, user.Username)
|
senderID = fmt.Sprintf("%d|%s", user.ID, user.Username)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查白名单,避免为被拒绝的用户下载附件
|
// check allowlist to avoid downloading attachments for rejected users
|
||||||
if !c.IsAllowed(senderID) {
|
if !c.IsAllowed(senderID) {
|
||||||
logger.DebugCF("telegram", "Message rejected by allowlist", map[string]any{
|
logger.DebugCF("telegram", "Message rejected by allowlist", map[string]any{
|
||||||
"user_id": senderID,
|
"user_id": senderID,
|
||||||
|
|
@ -221,9 +221,9 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes
|
||||||
|
|
||||||
content := ""
|
content := ""
|
||||||
mediaPaths := []string{}
|
mediaPaths := []string{}
|
||||||
localFiles := []string{} // 跟踪需要清理的本地文件
|
localFiles := []string{} // track local files that need cleanup
|
||||||
|
|
||||||
// 确保临时文件在函数返回时被清理
|
// ensure temp files are cleaned up when function returns
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, file := range localFiles {
|
for _, file := range localFiles {
|
||||||
if err := os.Remove(file); err != nil {
|
if err := os.Remove(file); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ func (info SkillInfo) validate() error {
|
||||||
|
|
||||||
type SkillsLoader struct {
|
type SkillsLoader struct {
|
||||||
workspace string
|
workspace string
|
||||||
workspaceSkills string // workspace skills (项目级别)
|
workspaceSkills string // workspace skills (project-level)
|
||||||
globalSkills string // 全局 skills (~/.picoclaw/skills)
|
globalSkills string // global skills (~/.picoclaw/skills)
|
||||||
builtinSkills string // 内置 skills
|
builtinSkills string // builtin skills
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSkillsLoader(workspace string, globalSkills string, builtinSkills string) *SkillsLoader {
|
func NewSkillsLoader(workspace string, globalSkills string, builtinSkills string) *SkillsLoader {
|
||||||
|
|
@ -120,7 +120,7 @@ func (sl *SkillsLoader) ListSkills() []SkillInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *SkillsLoader) LoadSkill(name string) (string, bool) {
|
func (sl *SkillsLoader) LoadSkill(name string) (string, bool) {
|
||||||
// 1. 优先从 workspace skills 加载(项目级别)
|
// 1. load from workspace skills first (project-level)
|
||||||
if sl.workspaceSkills != "" {
|
if sl.workspaceSkills != "" {
|
||||||
skillFile := filepath.Join(sl.workspaceSkills, name, "SKILL.md")
|
skillFile := filepath.Join(sl.workspaceSkills, name, "SKILL.md")
|
||||||
if content, err := os.ReadFile(skillFile); err == nil {
|
if content, err := os.ReadFile(skillFile); err == nil {
|
||||||
|
|
@ -128,7 +128,7 @@ func (sl *SkillsLoader) LoadSkill(name string) (string, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 其次从全局 skills 加载 (~/.picoclaw/skills)
|
// 2. then load from global skills (~/.picoclaw/skills)
|
||||||
if sl.globalSkills != "" {
|
if sl.globalSkills != "" {
|
||||||
skillFile := filepath.Join(sl.globalSkills, name, "SKILL.md")
|
skillFile := filepath.Join(sl.globalSkills, name, "SKILL.md")
|
||||||
if content, err := os.ReadFile(skillFile); err == nil {
|
if content, err := os.ReadFile(skillFile); err == nil {
|
||||||
|
|
@ -136,7 +136,7 @@ func (sl *SkillsLoader) LoadSkill(name string) (string, bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 最后从内置 skills 加载
|
// 3. finally load from builtin skills
|
||||||
if sl.builtinSkills != "" {
|
if sl.builtinSkills != "" {
|
||||||
skillFile := filepath.Join(sl.builtinSkills, name, "SKILL.md")
|
skillFile := filepath.Join(sl.builtinSkills, name, "SKILL.md")
|
||||||
if content, err := os.ReadFile(skillFile); err == nil {
|
if content, err := os.ReadFile(skillFile); err == nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue