feat: enhance event handling and add scheduling capabilities

- Introduced a new scheduler to manage timed tasks within the event dispatcher.
- Updated the dispatcher to support the new scheduler, allowing for improved event processing.
- Enhanced action serialization in the OneBot11 adapter to convert message chains to the appropriate format.
- Added new dependencies for cron scheduling and other indirect packages in go.mod and go.sum.
- Improved logging for event publishing and handler matching, providing better insights during execution.
- Refactored plugin loading to include scheduled job management.
This commit is contained in:
lafay
2026-01-05 04:33:30 +08:00
parent d16261e6bd
commit 64cd81b7f1
14 changed files with 2130 additions and 27 deletions

View File

@@ -303,4 +303,4 @@ func BuildSetGroupCard(groupID, userID int64, card string) map[string]interface{
"user_id": userID,
"card": card,
}
}
}

View File

@@ -227,9 +227,23 @@ func (a *Adapter) SerializeAction(action protocol.Action) ([]byte, error) {
zap.String("hint", "This action type may not be supported by OneBot11"))
}
// 复制参数并转换消息链
params := make(map[string]interface{})
for k, v := range action.GetParams() {
// 检查是否是消息链
if k == "message" {
if chain, ok := v.(protocol.MessageChain); ok {
// 转换为 OneBot11 格式
params[k] = ConvertMessageChainToOB11(chain)
continue
}
}
params[k] = v
}
ob11Action := &OB11Action{
Action: ob11ActionName,
Params: action.GetParams(),
Params: params,
}
return sonic.Marshal(ob11Action)

View File

@@ -0,0 +1,238 @@
package onebot11
import (
"fmt"
"cellbot/internal/protocol"
)
// ConvertMessageChainToOB11 将通用消息链转换为 OneBot11 格式
func ConvertMessageChainToOB11(chain protocol.MessageChain) interface{} {
if len(chain) == 0 {
return ""
}
// 转换为 OneBot11 消息段格式
segments := make([]MessageSegment, 0, len(chain))
for _, seg := range chain {
ob11Seg := convertSegmentToOB11(seg)
if ob11Seg != nil {
segments = append(segments, *ob11Seg)
}
}
// 如果只有一个文本消息段,直接返回文本字符串
if len(segments) == 1 && segments[0].Type == SegmentTypeText {
if text, ok := segments[0].Data["text"].(string); ok {
return text
}
}
return segments
}
// convertSegmentToOB11 将通用消息段转换为 OneBot11 消息段
func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
switch seg.Type {
case protocol.SegmentTypeText:
// 文本消息段
return &MessageSegment{
Type: SegmentTypeText,
Data: map[string]interface{}{
"text": seg.Data["text"],
},
}
case protocol.SegmentTypeMention:
// @提及OneBot12 -> OneBot11
userID := seg.Data["user_id"]
return &MessageSegment{
Type: SegmentTypeAt,
Data: map[string]interface{}{
"qq": userID,
},
}
case protocol.SegmentTypeAt:
// OneBot11 兼容格式
userID, ok := seg.Data["user_id"]
if !ok {
userID = seg.Data["qq"]
}
return &MessageSegment{
Type: SegmentTypeAt,
Data: map[string]interface{}{
"qq": userID,
},
}
case protocol.SegmentTypeImage:
// 图片消息段
fileID, ok := seg.Data["file_id"].(string)
if !ok {
// 兼容 file 字段
fileID, _ = seg.Data["file"].(string)
}
return &MessageSegment{
Type: SegmentTypeImage,
Data: map[string]interface{}{
"file": fileID,
},
}
case protocol.SegmentTypeVoice:
// 语音消息段OneBot12 -> OneBot11
fileID, ok := seg.Data["file_id"].(string)
if !ok {
fileID, _ = seg.Data["file"].(string)
}
return &MessageSegment{
Type: SegmentTypeRecord,
Data: map[string]interface{}{
"file": fileID,
},
}
case protocol.SegmentTypeRecord:
// OneBot11 兼容格式
fileID, ok := seg.Data["file"].(string)
if !ok {
fileID, _ = seg.Data["file_id"].(string)
}
return &MessageSegment{
Type: SegmentTypeRecord,
Data: map[string]interface{}{
"file": fileID,
},
}
case protocol.SegmentTypeVideo:
// 视频消息段
fileID, ok := seg.Data["file_id"].(string)
if !ok {
fileID, _ = seg.Data["file"].(string)
}
return &MessageSegment{
Type: SegmentTypeVideo,
Data: map[string]interface{}{
"file": fileID,
},
}
case protocol.SegmentTypeReply:
// 回复消息段
messageID := seg.Data["message_id"]
return &MessageSegment{
Type: SegmentTypeReply,
Data: map[string]interface{}{
"id": messageID,
},
}
case protocol.SegmentTypeFace:
// 表情消息段
faceID := seg.Data["id"]
return &MessageSegment{
Type: SegmentTypeFace,
Data: map[string]interface{}{
"id": faceID,
},
}
default:
// 其他类型,尝试直接转换
return &MessageSegment{
Type: seg.Type,
Data: seg.Data,
}
}
}
// ConvertOB11ToMessageChain 将 OneBot11 消息段转换为通用消息链
func ConvertOB11ToMessageChain(ob11Message interface{}) (protocol.MessageChain, error) {
chain := protocol.MessageChain{}
// 如果是字符串,转换为文本消息段
if str, ok := ob11Message.(string); ok {
return protocol.NewMessageChain(protocol.NewTextSegment(str)), nil
}
// 如果是数组,解析为消息段数组
if segments, ok := ob11Message.([]MessageSegment); ok {
for _, seg := range segments {
genericSeg := convertOB11SegmentToGeneric(seg)
chain = append(chain, genericSeg)
}
return chain, nil
}
// 如果是接口数组,尝试转换
if segments, ok := ob11Message.([]interface{}); ok {
for _, seg := range segments {
if segMap, ok := seg.(map[string]interface{}); ok {
segType, _ := segMap["type"].(string)
segData, _ := segMap["data"].(map[string]interface{})
genericSeg := convertOB11SegmentToGeneric(MessageSegment{
Type: segType,
Data: segData,
})
chain = append(chain, genericSeg)
}
}
return chain, nil
}
return nil, fmt.Errorf("unsupported message format: %T", ob11Message)
}
// convertOB11SegmentToGeneric 将 OneBot11 消息段转换为通用消息段
func convertOB11SegmentToGeneric(seg MessageSegment) protocol.MessageSegment {
switch seg.Type {
case SegmentTypeText:
return protocol.NewTextSegment(seg.Data["text"].(string))
case SegmentTypeAt:
// OneBot11 @ 转换为 OneBot12 mention
userID := seg.Data["qq"]
return protocol.NewMentionSegment(userID)
case SegmentTypeImage:
fileID, ok := seg.Data["file"].(string)
if !ok {
fileID, _ = seg.Data["file_id"].(string)
}
return protocol.NewImageSegment(fileID)
case SegmentTypeRecord:
// OneBot11 record 转换为 OneBot12 voice
fileID, ok := seg.Data["file"].(string)
if !ok {
fileID, _ = seg.Data["file_id"].(string)
}
return protocol.MessageSegment{
Type: protocol.SegmentTypeVoice,
Data: map[string]interface{}{
"file_id": fileID,
},
}
case SegmentTypeReply:
messageID := seg.Data["id"]
return protocol.NewReplySegment(messageID)
case SegmentTypeFace:
return protocol.MessageSegment{
Type: protocol.SegmentTypeFace,
Data: map[string]interface{}{
"id": seg.Data["id"],
},
}
default:
// 其他类型,直接转换
return protocol.MessageSegment{
Type: seg.Type,
Data: seg.Data,
}
}
}