From 93538afaebf9f6cb186f4378fec9b2d9c4a72fc7 Mon Sep 17 00:00:00 2001 From: Sheyiyuan <2125107118@qq.com> Date: Tue, 1 Apr 2025 01:20:35 +0800 Subject: [PATCH] =?UTF-8?q?modify=EF=BC=9A=E5=BA=95=E5=B1=82=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=8F=98=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/api.go | 224 +++++++++++++++++++++++++++++++++++++---- core/app_admin.go | 22 ++-- core/events_handler.go | 134 +++++++++++++++++------- core/web_service.go | 7 +- database/database.go | 80 +-------------- typed/typed.go | 22 ++-- wba/tool.go | 5 + wba/wind.go | 115 ++++++++++++++------- 8 files changed, 409 insertions(+), 200 deletions(-) create mode 100644 wba/tool.go diff --git a/core/api.go b/core/api.go index 34f34dc..6b8d415 100644 --- a/core/api.go +++ b/core/api.go @@ -5,6 +5,7 @@ import ( "ProjectWIND/database" "ProjectWIND/wba" "crypto/rand" + "encoding/json" "fmt" "strings" ) @@ -26,6 +27,20 @@ type apiInfo struct{} //1.无响应API,使用ws协议处理 // UnsafelySendMsg 发送消息(自动判断消息类型) +// +// 注意:该API不安全,除非你知道你在做什么。 +// +// 参数: +// +// - messageType: 消息类型,可选值为 "private" 和 "group +// +// - groupId: 群号,当messageType为"group"时必填 +// +// - userId: 用户ID,当messageType为"private"时必填 +// +// - message: 要发送的消息内容,类型为字符串 +// +// - autoEscape: 是否自动转义(解析消息内容中的CQ码),可选值为 true 和 false func (a *apiInfo) UnsafelySendMsg(messageType string, groupId int64, userId int64, message string, autoEscape bool) { // 构建发送消息的JSON数据 var messageData wba.APIRequestInfo @@ -59,6 +74,16 @@ func (a *apiInfo) UnsafelySendMsg(messageType string, groupId int64, userId int6 } // UnsafelySendPrivateMsg 发送私聊消息 +// +// 注意:该API不安全,除非你知道你在做什么。 +// +// 参数: +// +// - userId: 用户ID +// +// - message: 要发送的消息内容,类型为字符串 +// +// - autoEscape: 是否自动转义(解析消息内容中的CQ码),可选值为 true 和 false func (a *apiInfo) UnsafelySendPrivateMsg(userId int64, message string, autoEscape bool) { // 构建发送消息的JSON数据 var messageData wba.APIRequestInfo @@ -77,6 +102,16 @@ func (a *apiInfo) UnsafelySendPrivateMsg(userId int64, message string, autoEscap } // UnsafelySendGroupMsg 发送群消息 +// +// 注意:该API不安全,除非你知道你在做什么。 +// +// 参数: +// +// - groupId: 群号 +// +// - message: 要发送的消息内容,类型为字符串 +// +// - autoEscape: 是否自动转义(解析消息内容中的CQ码),可选值为 true 和 false func (a *apiInfo) UnsafelySendGroupMsg(groupId int64, message string, autoEscape bool) { // 构建发送消息的JSON数据 var messageData wba.APIRequestInfo @@ -95,6 +130,14 @@ func (a *apiInfo) UnsafelySendGroupMsg(groupId int64, message string, autoEscape } // SendMsg 回复消息(自动判断消息类型) +// +// 参数: +// +// - msg: 消息事件信息 +// +// - message: 要发送的消息内容,类型为字符串 +// +// - autoEscape: 是否自动转义(解析消息内容中的CQ码),可选值为 true 和 false func (a *apiInfo) SendMsg(msg wba.MessageEventInfo, message string, autoEscape bool) { // 构建发送消息的JSON数据 var messageData wba.APIRequestInfo @@ -131,6 +174,14 @@ func (a *apiInfo) SendMsg(msg wba.MessageEventInfo, message string, autoEscape b } // SendPrivateMsg 回复私聊消息 +// +// 参数: +// +// - msg: 原始消息事件信息 +// +// - message: 要发送的消息内容,类型为字符串 +// +// - autoEscape: 是否自动转义,可选值为 true 和 false func (a *apiInfo) SendPrivateMsg(msg wba.MessageEventInfo, message string, autoEscape bool) { // 构建发送消息的JSON数据 var messageData wba.APIRequestInfo @@ -149,6 +200,14 @@ func (a *apiInfo) SendPrivateMsg(msg wba.MessageEventInfo, message string, autoE } // SendGroupMsg 回复群消息 +// +// 参数: +// +// - msg: 原始消息事件信息 +// +// - message: 要发送的消息内容,类型为字符串 +// +// - autoEscape: 是否自动转义,可选值为 true 和 false func (a *apiInfo) SendGroupMsg(msg wba.MessageEventInfo, message string, autoEscape bool) { // 构建发送消息的JSON数据 var messageData wba.APIRequestInfo @@ -167,6 +226,12 @@ func (a *apiInfo) SendGroupMsg(msg wba.MessageEventInfo, message string, autoEsc } // UnsafelyDeleteMsg 撤回消息 +// +// 注意:该API不安全,除非你知道你在做什么。 +// +// 参数: +// +// - messageId: 要撤回的消息ID func (a *apiInfo) UnsafelyDeleteMsg(messageId int32) { // 构建删除消息的JSON数据 var messageData wba.APIRequestInfo @@ -182,6 +247,10 @@ func (a *apiInfo) UnsafelyDeleteMsg(messageId int32) { } // DeleteMsg 撤回消息 +// +// 参数: +// +// - msg: 原始消息事件信息 func (a *apiInfo) DeleteMsg(msg wba.MessageEventInfo) { // 构建删除消息的JSON数据 var messageData wba.APIRequestInfo @@ -197,6 +266,12 @@ func (a *apiInfo) DeleteMsg(msg wba.MessageEventInfo) { } // SendLike 发送赞 +// +// 参数: +// +// - userId: 要赞的用户ID +// +// - times: 赞的次数 func (a *apiInfo) SendLike(userId int64, times int) { // 构建发送赞的JSON数据 var messageData wba.APIRequestInfo @@ -213,6 +288,14 @@ func (a *apiInfo) SendLike(userId int64, times int) { } // SetGroupKick 将指定用户移出群聊(需要群主或管理员权限) +// +// 参数: +// +// - groupId: 群号 +// +// - userId: 用户ID +// +// - rejectAddRequest: 是否拒绝该用户的后续加群请求,可选值为 true 和 false func (a *apiInfo) SetGroupKick(groupId int64, userId int64, rejectAddRequest bool) { var messageData wba.APIRequestInfo messageData.Action = "set_group_kick" @@ -229,6 +312,14 @@ func (a *apiInfo) SetGroupKick(groupId int64, userId int64, rejectAddRequest boo } // SetGroupBan 将指定用户禁言(需要群主或管理员权限) +// +// 参数: +// +// - groupId: 群号 +// +// - userId: 用户ID +// +// - duration: 禁言时长,单位为秒,0表示取消禁言 func (a *apiInfo) SetGroupBan(groupId int64, userId int64, duration int32) { var messageData wba.APIRequestInfo messageData.Action = "set_group_ban" @@ -245,6 +336,12 @@ func (a *apiInfo) SetGroupBan(groupId int64, userId int64, duration int32) { } // SetGroupWholeBan 设置全员禁言(需要群主或管理员权限) +// +// 参数: +// +// - groupId: 群号 +// +// - enable: 是否启用全员禁言,可选值为 true 和 false func (a *apiInfo) SetGroupWholeBan(groupId int64, enable bool) { var messageData wba.APIRequestInfo messageData.Action = "set_group_whole_ban" @@ -260,6 +357,14 @@ func (a *apiInfo) SetGroupWholeBan(groupId int64, enable bool) { } // SetGroupAdmin 设置群管理员(需要群主权限) +// +// 参数: +// +// - groupId: 群号 +// +// - userId: 用户ID +// +// - enable: 是否设置为管理员,可选值为 true 和 false func (a *apiInfo) SetGroupAdmin(groupId int64, userId int64, enable bool) { var messageData wba.APIRequestInfo messageData.Action = "set_group_admin" @@ -275,7 +380,15 @@ func (a *apiInfo) SetGroupAdmin(groupId int64, userId int64, enable bool) { return } -// SetGroupCard 设置群名片(需要群主或管理员权限) +// SetGroupCard 设置群名片(可能需要群主或管理员权限) +// +// 参数: +// +// - groupId: 群号 +// +// - userId: 用户ID +// +// - card: 新的群名片 func (a *apiInfo) SetGroupCard(groupId int64, userId int64, card string) { var messageData wba.APIRequestInfo messageData.Action = "set_group_card" @@ -292,6 +405,12 @@ func (a *apiInfo) SetGroupCard(groupId int64, userId int64, card string) { } // SetGroupName 设置群名称(可能需要群主或管理员权限) +// +// 参数: +// +// - groupId: 群号 +// +// - groupName: 新的群名称 func (a *apiInfo) SetGroupName(groupId int64, groupName string) { var messageData wba.APIRequestInfo messageData.Action = "set_group_name" @@ -307,6 +426,12 @@ func (a *apiInfo) SetGroupName(groupId int64, groupName string) { } // SetGroupLeave 退出群聊 +// +// 参数: +// +// - groupId: 群号 +// +// - isDismiss: 是否解散群聊,仅当退出的是群主时有效,可选值为 true 和 false func (a *apiInfo) SetGroupLeave(groupId int64, isDismiss bool) { var messageData wba.APIRequestInfo messageData.Action = "set_group_leave" @@ -322,6 +447,14 @@ func (a *apiInfo) SetGroupLeave(groupId int64, isDismiss bool) { } // SetGroupSpecialTitle 设置群专属头衔(需要群主权限) +// +// 参数: +// +// - groupId: 群号 +// +// - userId: 用户ID +// +// - specialTitle: 新的专属头衔 func (a *apiInfo) SetGroupSpecialTitle(groupId int64, userId int64, specialTitle string) { var messageData wba.APIRequestInfo messageData.Action = "set_group_special_title" @@ -339,6 +472,14 @@ func (a *apiInfo) SetGroupSpecialTitle(groupId int64, userId int64, specialTitle } // SetFriendAddRequest 处理加好友请求 +// +// 参数: +// +// - flag: 请求标识,由上报的事件中获得 +// +// - approve: 是否同意请求,可选值为 true 和 false +// +// - remark: 设置好友的备注信息 func (a *apiInfo) SetFriendAddRequest(flag string, approve bool, remark string) { var messageData wba.APIRequestInfo messageData.Action = "set_friend_add_request" @@ -355,6 +496,16 @@ func (a *apiInfo) SetFriendAddRequest(flag string, approve bool, remark string) } // SetGroupAddRequest 处理加群请求/邀请 +// +// 参数: +// +// - flag: 请求标识,由上报的事件中获得 +// +// - subType: 子类型,可能是 "invite" 或 "add", 由上报的事件中获得 +// +// - approve: 是否同意请求,可选值为 true 和 false +// +// - reason: 拒绝请求的原因,仅当 approve 为 false 时有效 func (a *apiInfo) SetGroupAddRequest(flag string, subType string, approve bool, reason string) { var messageData wba.APIRequestInfo messageData.Action = "set_group_add_request" @@ -398,7 +549,7 @@ func (a *apiInfo) CleanCache() { return } -// 2.有响应API,需添加echo字段 +// 2.有响应API,需添加echo字段,统一返回响应结构体 // GetLoginInfo 获取登录信息 func (a *apiInfo) GetLoginInfo() (Response wba.APIResponseInfo) { @@ -783,13 +934,22 @@ func (a *apiInfo) GetStatus() (Response wba.APIResponseInfo) { /* 关于LOG模块的说明 -1.日志模块使用go-logging库,日志级别分为DEBUG、Info、Warn、Error。 +1.日志模块级别分为TRACE、DEBUG、INFO、NOTICE、WARN、ERROR五个级别,默认级别为INFO。 -2.日志模块提供LogWith方法,可以自定义日志级别,调用级别为DEBUG时,会打印输出调用者的文件名、函数名、行号。 +2.日志模块提供LogWith方法,可以自定义日志级别。 3.日志模块提供Log方法,默认日志级别为INFO。 */ +// LogWith 打印日志(带级别) +// +// 参数: +// - level: 日志级别,支持"TRACE"、"DEBUG"、"INFO"、"NOTICE"、 "WARN"、"ERROR" +// - content: 日志内容 +// - args: 可选参数,用于格式化日志内容 +// +// 返回值: +// - 无 func (a *apiInfo) LogWith(level string, content string, args ...interface{}) { level = strings.ToLower(level) switch level { @@ -814,22 +974,42 @@ func (a *apiInfo) LogWith(level string, content string, args ...interface{}) { } } +// Log 打印日志 +// +// 参数: +// - content: 日志内容 +// - args: 可选参数,用于格式化日志内容 func (a *apiInfo) Log(content string, args ...interface{}) { LOG.Info(content, args...) } +// MsgUnmarshal 解析消息 +// +// 参数: +// - messageJSON: 从数据库中获取的JSON序列化后的消息字符串 +// +// 返回值: +// - wba.MessageEventInfo: 解析后的消息事件信息,解析失败时返回空结构体 +func (a *apiInfo) MsgUnmarshal(messageJSON string) (msg wba.MessageEventInfo) { + err := json.Unmarshal([]byte(messageJSON), &msg) + if err != nil { + return wba.MessageEventInfo{} + } + return msg +} + //database模块 // //数据库部分允许字符串变量的读写操作,允许读取配置项操作 type databaseInfo struct{} func (dbi *databaseInfo) varSet(app wba.AppInfo, datamap string, unit string, id string, key string, value string) { - database.Set(app.Name, datamap, unit, id, key, value) + database.Set(app.AppKey.Name, datamap, unit, id, key, value) } func (dbi *databaseInfo) SetUserVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string, value string) { id := fmt.Sprintf("%d", msg.UserId) - dbi.varSet(app, app.Name, "user", id, key, value) + dbi.varSet(app, app.AppKey.Name, "user", id, key, value) } func (dbi *databaseInfo) SetGroupVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string, value string) { @@ -840,7 +1020,7 @@ func (dbi *databaseInfo) SetGroupVariable(app wba.AppInfo, msg wba.MessageEventI if msg.MessageType == "private" { id = "user_" + fmt.Sprintf("%d", msg.UserId) } - dbi.varSet(app, app.Name, "group", id, key, value) + dbi.varSet(app, app.AppKey.Name, "group", id, key, value) } func (dbi *databaseInfo) SetOutUserVariable(app wba.AppInfo, datamap string, msg wba.MessageEventInfo, key string, value string) { @@ -860,15 +1040,15 @@ func (dbi *databaseInfo) SetOutGroupVariable(app wba.AppInfo, datamap string, ms } func (dbi *databaseInfo) UnsafelySetUserVariable(app wba.AppInfo, id string, key string, value string) { - dbi.varSet(app, app.Name, "user", id, key, value) + dbi.varSet(app, app.AppKey.Name, "user", id, key, value) } func (dbi *databaseInfo) UnsafelySetGroupVariable(app wba.AppInfo, id string, key string, value string) { - dbi.varSet(app, app.Name, "group", id, key, value) + dbi.varSet(app, app.AppKey.Name, "group", id, key, value) } func (dbi *databaseInfo) UnsafelySetGlobalVariable(app wba.AppInfo, id string, key string, value string) { - dbi.varSet(app, app.Name, "global", id, key, value) + dbi.varSet(app, app.AppKey.Name, "global", id, key, value) } func (dbi *databaseInfo) UnsafelySetOutUserVariable(app wba.AppInfo, datamap string, id string, key string, value string) { @@ -884,7 +1064,7 @@ func (dbi *databaseInfo) UnsafelySetOutGlobalVariable(app wba.AppInfo, datamap s } func (dbi *databaseInfo) varGet(app wba.AppInfo, datamap string, unit string, id string, key string) (string, bool) { - res, ok := database.Get(app.Name, datamap, unit, id, key, false) + res, ok := database.Get(app.AppKey.Name, datamap, unit, id, key, false) if !ok { return "", false } @@ -897,7 +1077,7 @@ func (dbi *databaseInfo) varGet(app wba.AppInfo, datamap string, unit string, id func (dbi *databaseInfo) GetUserVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string) (string, bool) { id := fmt.Sprintf("%d", msg.UserId) - return dbi.varGet(app, app.Name, "user", id, key) + return dbi.varGet(app, app.AppKey.Name, "user", id, key) } func (dbi *databaseInfo) GetGroupVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string) (string, bool) { @@ -908,7 +1088,7 @@ func (dbi *databaseInfo) GetGroupVariable(app wba.AppInfo, msg wba.MessageEventI if msg.MessageType == "private" { id = "user_" + fmt.Sprintf("%d", msg.UserId) } - return dbi.varGet(app, app.Name, "group", id, key) + return dbi.varGet(app, app.AppKey.Name, "group", id, key) } func (dbi *databaseInfo) GetOutUserVariable(app wba.AppInfo, datamap string, msg wba.MessageEventInfo, key string) (string, bool) { @@ -928,15 +1108,15 @@ func (dbi *databaseInfo) GetOutGroupVariable(app wba.AppInfo, datamap string, ms } func (dbi *databaseInfo) UnsafelyGetUserVariable(app wba.AppInfo, id string, key string) (string, bool) { - return dbi.varGet(app, app.Name, "user", id, key) + return dbi.varGet(app, app.AppKey.Name, "user", id, key) } func (dbi *databaseInfo) UnsafelyGetGroupVariable(app wba.AppInfo, id string, key string) (string, bool) { - return dbi.varGet(app, app.Name, "group", id, key) + return dbi.varGet(app, app.AppKey.Name, "group", id, key) } func (dbi *databaseInfo) UnsafelyGetGlobalVariable(app wba.AppInfo, id string, key string) (string, bool) { - return dbi.varGet(app, app.Name, "global", id, key) + return dbi.varGet(app, app.AppKey.Name, "global", id, key) } func (dbi *databaseInfo) UnsafelyGetOutUserVariable(app wba.AppInfo, datamap string, id string, key string) (string, bool) { @@ -952,7 +1132,7 @@ func (dbi *databaseInfo) UnsafelyGetOutGlobalVariable(app wba.AppInfo, datamap s } func (dbi *databaseInfo) GetIntConfig(app wba.AppInfo, datamap string, key string) (int64, bool) { - res, ok := database.Get(app.Name, datamap, "config", "number", key, true) + res, ok := database.Get(app.AppKey.Name, datamap, "config", "number", key, true) if !ok { return 0, false } @@ -964,7 +1144,7 @@ func (dbi *databaseInfo) GetIntConfig(app wba.AppInfo, datamap string, key strin } func (dbi *databaseInfo) GetStringConfig(app wba.AppInfo, datamap string, key string) (string, bool) { - res, ok := database.Get(app.Name, datamap, "config", "string", key, true) + res, ok := database.Get(app.AppKey.Name, datamap, "config", "string", key, true) if !ok { return "", false } @@ -976,7 +1156,7 @@ func (dbi *databaseInfo) GetStringConfig(app wba.AppInfo, datamap string, key st } func (dbi *databaseInfo) GetFloatConfig(app wba.AppInfo, datamap string, key string) (float64, bool) { - res, ok := database.Get(app.Name, datamap, "config", "float", key, true) + res, ok := database.Get(app.AppKey.Name, datamap, "config", "float", key, true) if !ok { return 0, false } @@ -988,7 +1168,7 @@ func (dbi *databaseInfo) GetFloatConfig(app wba.AppInfo, datamap string, key str } func (dbi *databaseInfo) GetIntSliceConfig(app wba.AppInfo, datamap string, key string) ([]int64, bool) { - res, ok := database.Get(app.Name, datamap, "config", "number_slice", key, true) + res, ok := database.Get(app.AppKey.Name, datamap, "config", "number_slice", key, true) if !ok { return nil, false } @@ -1000,7 +1180,7 @@ func (dbi *databaseInfo) GetIntSliceConfig(app wba.AppInfo, datamap string, key } func (dbi *databaseInfo) GetStringSliceConfig(app wba.AppInfo, datamap string, key string) ([]string, bool) { - res, ok := database.Get(app.Name, datamap, "config", "string_slice", key, true) + res, ok := database.Get(app.AppKey.Name, datamap, "config", "string_slice", key, true) if !ok { return nil, false } @@ -1012,7 +1192,7 @@ func (dbi *databaseInfo) GetStringSliceConfig(app wba.AppInfo, datamap string, k } func (dbi *databaseInfo) UnsafelyCreatePublicDatamap(app wba.AppInfo, datamapId string) { - appName := app.Name + appName := app.AppKey.Name database.CreatePublicDatamap(appName, datamapId) } diff --git a/core/app_admin.go b/core/app_admin.go index 0d6d2a7..95228dc 100644 --- a/core/app_admin.go +++ b/core/app_admin.go @@ -2,7 +2,6 @@ package core import ( "ProjectWIND/LOG" - "ProjectWIND/typed" "ProjectWIND/wba" "os" "path/filepath" @@ -12,12 +11,13 @@ import ( ) var CmdMap = make([]map[string]wba.Cmd, 4) -var AppMap = make(map[typed.AppKey]wba.AppInfo) +var AppMap = make(map[wba.AppKey]wba.AppInfo) +// ReloadApps 重新加载应用 func ReloadApps() (total int, success int) { // 清空AppMap和CmdMap CmdMap = make([]map[string]wba.Cmd, 4) - AppMap = make(map[typed.AppKey]wba.AppInfo) + AppMap = make(map[wba.AppKey]wba.AppInfo) appsDir := "./data/app/" appFiles, err := os.ReadDir(appsDir) total = 0 @@ -36,6 +36,7 @@ func ReloadApps() (total int, success int) { return total, success } +// reloadAPP 重新加载单个应用 func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta int) { if file.IsDir() { return 0, 0 @@ -61,6 +62,7 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i wbaObj := runtime.NewObject() wsp := runtime.NewObject() wsd := runtime.NewObject() + wst := runtime.NewObject() _ = runtime.Set("wba", wbaObj) _ = wbaObj.Set("NewApp", wba.NewApp) _ = wbaObj.Set("WithName", wba.WithName) @@ -73,6 +75,8 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i _ = wbaObj.Set("WithRule", wba.WithRule) _ = wbaObj.Set("wsp", wsp) _ = wbaObj.Set("wsd", wsd) + _ = wbaObj.Set("wst", wst) + //WSP注册 _ = wsp.Set("UnsafelySendMsg", AppApi.UnsafelySendMsg) _ = wsp.Set("UnsafelySendPrivateMsg", AppApi.UnsafelySendPrivateMsg) _ = wsp.Set("UnsafelySendGroupMsg", AppApi.UnsafelySendGroupMsg) @@ -113,8 +117,12 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i _ = wsp.Set("CanSendRecord", AppApi.CanSendRecord) _ = wsp.Set("SetRestart", AppApi.SetRestart) _ = wsp.Set("CleanCache", AppApi.CleanCache) - _ = wsp.Set("GetLoginInfo", AppApi.LogWith) _ = wsp.Set("GetVersionInfo", AppApi.GetVersionInfo) + //WST注册 + _ = wst.Set("LogWith", AppApi.LogWith) + _ = wst.Set("Log", AppApi.Log) + _ = wst.Set("MsgMarshal", AppApi.MsgUnmarshal) + //WSD注册 _ = wsd.Set("SetUserVariable", DatabaseApi.SetUserVariable) _ = wsd.Set("SetGroupVariable", DatabaseApi.SetGroupVariable) _ = wsd.Set("SetOutUserVariable", DatabaseApi.SetOutUserVariable) @@ -196,14 +204,14 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i return 1, 0 } - AppMap[typed.AppKey{AppName: appInfo.Name, AppType: appInfo.AppType, AppVersion: appInfo.Version, AppLevel: checkAppLevel(appInfo)}] = appInfo - cmdIndex := AppTypeToInt(appInfo.AppType) + AppMap[wba.AppKey{Name: appInfo.AppKey.Name, Type: appInfo.AppKey.Type, Version: appInfo.AppKey.Version, Level: checkAppLevel(appInfo)}] = appInfo + cmdIndex := AppTypeToInt(appInfo.AppKey.Type) // 合并命令 CmdMap[cmdIndex] = mergeMaps(CmdMap[cmdIndex], appInfo.CmdMap) // 注册定时任务 for _, task := range appInfo.ScheduledTasks { - RegisterCron(appInfo.Name, task) + RegisterCron(appInfo.AppKey.Name, task) } LOG.Info("JS应用 %s 加载成功", pluginPath) diff --git a/core/events_handler.go b/core/events_handler.go index 37b721b..5046f3e 100644 --- a/core/events_handler.go +++ b/core/events_handler.go @@ -2,9 +2,12 @@ package core import ( "ProjectWIND/LOG" + "ProjectWIND/database" + "ProjectWIND/typed" "ProjectWIND/wba" "encoding/json" "fmt" + "strconv" "strings" ) @@ -16,17 +19,8 @@ func HandleMessage(msgJson []byte) { } // 处理消息 LOG.Info("收到消息:(来自:%v-%v:%v-%v)%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, msg.RawMessage) - cmd, args := CmdSplit(msg) - for _, cmdList := range CmdMap { - _, ok := cmdList[cmd] - if ok { - LOG.Debug("执行命令:%v %v", cmd, args) - cmdList[cmd].Solve(args, msg) - break - } - } - - // TODO: 处理消息内容 + CmdHandle(msg) + fmt.Printf("%#v\n", msg.Message) } func HandleNotice(msgJson []byte) { @@ -57,35 +51,101 @@ func HandleMetaEvent(msgJson []byte) { } func CmdSplit(msg wba.MessageEventInfo) (string, []string) { - text := msg.RawMessage - if strings.HasPrefix(text, fmt.Sprintf("[CQ:at,qq=%d]", msg.SelfId)) { - text = strings.TrimPrefix(text, fmt.Sprintf("[CQ:at,qq=%d]", msg.SelfId)) - } else { - if statusCheck(msg) { - return "", []string{} - } - } - //检查有无application.CmdMap中的命令前缀 - for _, prefix := range cmdPrefix { - if strings.HasPrefix(text, prefix) { - text = strings.TrimPrefix(text, prefix) - for cmdList := range CmdMap { - for cmd := range CmdMap[cmdList] { - if strings.HasPrefix(text, cmd) { - text = strings.TrimPrefix(text, cmd) - text = strings.TrimPrefix(text, " ") - return cmd, strings.Split(text, " ") - } - } - } - } - } + //text := msg.RawMessage + //if strings.HasPrefix(text, fmt.Sprintf("[CQ:at,qq=%d]", msg.SelfId)) { + // text = strings.TrimPrefix(text, fmt.Sprintf("[CQ:at,qq=%d]", msg.SelfId)) + //} else { + // if !statusCheck(msg) { + // return "", []string{} + // } + //} + ////检查有无application.CmdMap中的命令前缀 + //for _, prefix := range cmdPrefix { + // if strings.HasPrefix(text, prefix) { + // text = strings.TrimPrefix(text, prefix) + // for cmdList := range CmdMap { + // for cmd := range CmdMap[cmdList] { + // if strings.HasPrefix(text, cmd) { + // text = strings.TrimPrefix(text, cmd) + // text = strings.TrimPrefix(text, " ") + // return cmd, strings.Split(text, " ") + // } + // } + // } + // } + //} return "", []string{} } -func statusCheck(msg wba.MessageEventInfo) bool { - //TODO: 检查当前组群工作状态 - return false +func CmdHandle(msg wba.MessageEventInfo) { + // 获取消息的原始文本 + text := msg.GetText() + // 初始化会话工作状态 + WorkStatus := typed.SessionWorkSpace{} + + if msg.MessageType == "group" { + // 如果消息类型是群消息 + // 从数据库中获取工作状态信息 + WorkStatusJson, ok := database.MasterGet("WorkStatus", "global", strconv.FormatInt(msg.SelfId, 10), strconv.FormatInt(msg.GroupId, 10)) + // 尝试将获取到的工作状态信息反序列化为 WorkStatus 结构体 + err := json.Unmarshal([]byte(WorkStatusJson.(string)), &WorkStatus) + // 如果获取失败或反序列化失败 + if !ok || err != nil { + // 初始化一个新的工作状态 + WorkStatus = typed.SessionWorkSpace{ + SessionId: fmt.Sprintf("%d-%d", msg.SelfId, msg.GroupId), + Rule: "coc", + Enable: true, + AppEnable: make(map[wba.AppKey]bool), + CmdEnable: make(map[string]bool), + WorkLevel: 0, + } + // 将新的工作状态序列化为 JSON 字符串 + WorkStatusJson, err := json.Marshal(WorkStatus) + if err != nil { + // 如果序列化失败,记录错误信息 + LOG.Error("命令处理过程中,WorkStatusJson序列化失败: %v", err) + } + // 将序列化后的工作状态保存到数据库中 + database.MasterSet("WorkStatus", "global", strconv.FormatInt(msg.SelfId, 10), strconv.FormatInt(msg.GroupId, 10), string(WorkStatusJson)) + } + } + + // 调用 CmdSplit 函数将消息文本拆分为命令和参数 + cmd, args := CmdSplit(msg) + + // 检查是否找到了有效的命令 + if cmd != "" { + // 遍历命令映射表 CmdMap + for _, cmdList := range CmdMap { + for cmdKey, Cmd := range cmdList { + // 如果找到了匹配的命令 + if cmdKey == cmd { + // 检查会话工作状态 + if WorkStatus.SessionId != "" { + if !WorkStatus.Enable { + // 如果会话未启用,忽略该命令 + LOG.Debug("忽略指令:%s,当前会话中bot未启用", cmd) + continue + } + // 检查 APP 或命令是否启用,以及工作级别和规则是否匹配 + if !WorkStatus.AppEnable[Cmd.AppKey] || !WorkStatus.CmdEnable[cmd] || WorkStatus.WorkLevel > Cmd.AppKey.Level || (WorkStatus.Rule != Cmd.AppKey.Rule && WorkStatus.Rule != "none") { + // 如果不满足条件,忽略该命令 + LOG.Debug("忽略指令:%s,当前会话中APP或命令未启用", cmd) + continue + } + } + // 执行命令 + Cmd.Solve(args, msg) + // 记录执行的命令和参数 + LOG.Info("执行命令:%v %v", cmd, args) + } + } + } + } else { + // 如果未找到有效的命令,记录未找到命令的信息 + LOG.Info("未找到命令:%v", strings.Split(text, " ")[0]) + } } var cmdPrefix = []string{"/", "!", "/", "!", ".", "。"} diff --git a/core/web_service.go b/core/web_service.go index cb95f69..fdb64ba 100644 --- a/core/web_service.go +++ b/core/web_service.go @@ -10,11 +10,10 @@ import ( ) func WebServer(port string) *server.Hertz { - // 创建自定义日志记录器实例 - customLogger := &LOG.CustomLogger{} // 设置自定义日志记录器 - hlog.SetLogger(customLogger) + hlog.SetLevel(hlog.LevelFatal) h := server.Default(server.WithHostPorts("0.0.0.0:" + port)) + LOG.Info("WebUI已启动,监听端口:%v", port) h.Use(LoggingMiddleware()) h.GET("/index", func(ctx context.Context, c *app.RequestContext) { //返回webui/index.html @@ -41,6 +40,6 @@ func LoggingMiddleware() app.HandlerFunc { // 获取请求处理状态码 statusCode := ctx.Response.StatusCode() // 在请求处理后记录结束信息 - hlog.Debugf("收到网络请求 | IP: %s | Path: %s | Status: %d ", clientIP, fullPath, statusCode) + LOG.Debug("收到网络请求 | IP: %s | Path: %s | Status: %d ", clientIP, fullPath, statusCode) } } diff --git a/database/database.go b/database/database.go index 1270a98..74977ad 100644 --- a/database/database.go +++ b/database/database.go @@ -4,6 +4,7 @@ import ( "ProjectWIND/LOG" "encoding/json" "errors" + "fmt" "os" "os/signal" "path/filepath" @@ -155,7 +156,7 @@ func getCorePassword() string { return "" } config := make(map[string]string) - err = json.Unmarshal([]byte(dataJson), config) + err = json.Unmarshal([]byte(dataJson), &config) if err != nil { LOG.Error("[Error]Error while unmarshal data: %v", err) return "" @@ -467,6 +468,7 @@ func Start() { select { case <-dataChan: // 接收到信号,保存数据并退出程序 + fmt.Println("") LOG.Info("Received signal, saving data and exiting...") saveData(DB) os.Exit(0) @@ -588,79 +590,3 @@ func Set(appName string, datamap string, unit string, id string, key string, val } dataSet(appName, unit, id, key, value, true, false) } - -// func VarSet(app wba.AppInfo, datamap string, unit string, id string, key string, value string) { -// Set(app.Name, datamap, unit, id, key, value) -// } - -// func VarGet(app wba.AppInfo, datamap string, unit string, id string, key string) (string, bool) { -// res, ok := Get(app.Name, datamap, unit, id, key, false) -// if !ok { -// return "", false -// } -// resStr, ok := res.(string) -// if !ok { -// return "", false -// } -// return resStr, true -// } - -// func GetIntConfig(app wba.AppInfo, datamap string, key string) (int64, bool) { -// res, ok := Get(app.Name, datamap, "config", "number", key, true) -// if !ok { -// return 0, false -// } -// resInt, ok := res.(int64) -// if !ok { -// return 0, false -// } -// return resInt, true -// } - -// func GetStringConfig(app wba.AppInfo, datamap string, key string) (string, bool) { -// res, ok := Get(app.Name, datamap, "config", "string", key, true) -// if !ok { -// return "", false -// } -// resStr, ok := res.(string) -// if !ok { -// return "", false -// } -// return resStr, true -// } - -// func GetFloatConfig(app wba.AppInfo, datamap string, key string) (float64, bool) { -// res, ok := Get(app.Name, datamap, "config", "float", key, true) -// if !ok { -// return 0, false -// } -// resFloat, ok := res.(float64) -// if !ok { -// return 0, false -// } -// return resFloat, true -// } - -// func GetIntSliceConfig(app wba.AppInfo, datamap string, key string) ([]int64, bool) { -// res, ok := Get(app.Name, datamap, "config", "number_slice", key, true) -// if !ok { -// return nil, false -// } -// resSlice, ok := res.([]int64) -// if !ok { -// return nil, false -// } -// return resSlice, true -// } - -// func GetStringSliceConfig(app wba.AppInfo, datamap string, key string) ([]string, bool) { -// res, ok := Get(app.Name, datamap, "config", "string_slice", key, true) -// if !ok { -// return nil, false -// } -// resSlice, ok := res.([]string) -// if !ok { -// return nil, false -// } -// return resSlice, true -// } diff --git a/typed/typed.go b/typed/typed.go index 2dce859..2ca47a6 100644 --- a/typed/typed.go +++ b/typed/typed.go @@ -1,5 +1,7 @@ package typed +import "ProjectWIND/wba" + type CoreConfigInfo struct { CoreName string `json:"core_name"` Protocol Protocol `json:"protocol"` @@ -16,19 +18,11 @@ type Protocol struct { Enable bool `json:"enable"` } -type AppKey struct { - AppName string `json:"app_name"` - AppType string `json:"app_type"` - AppLevel int32 `json:"app_level"` - AppVersion string `json:"app_version"` -} - type SessionWorkSpace struct { - SessionId string `json:"session_id"` - SessionType string `json:"session_type"` - Rule string `json:"rule"` - Enable bool `json:"enable"` - AppEnable map[AppKey]bool `json:"app_enable"` - CmdEnable map[string]bool `json:"cmd_enable"` - WorkLevel int32 `json:"work_level"` + SessionId string `json:"session_id"` + Rule string `json:"rule"` + Enable bool `json:"enable"` + AppEnable map[wba.AppKey]bool `json:"app_enable"` + CmdEnable map[string]bool `json:"cmd_enable"` + WorkLevel int32 `json:"work_level"` } diff --git a/wba/tool.go b/wba/tool.go new file mode 100644 index 0000000..2faf1bd --- /dev/null +++ b/wba/tool.go @@ -0,0 +1,5 @@ +package wba + +type WindStandardTools interface { + MsgUnmarshal(message string) (msg MessageEventInfo) +} diff --git a/wba/wind.go b/wba/wind.go index 0deecd7..4c3fd7d 100644 --- a/wba/wind.go +++ b/wba/wind.go @@ -1,13 +1,14 @@ package wba import ( + "encoding/json" "fmt" ) type APP interface { Get() AppInfo - Init(api WindStandardProtocolAPI) error - InitWSD(api WindStandardDataBaseAPI) error + //Init(api WindStandardProtocolAPI) error + //InitWSD(api WindStandardDataBaseAPI) error } // WindStandardProtocolAPI Wind标准协议API,提供了onebot11标准中的API接口。 @@ -477,7 +478,7 @@ type WindStandardDataBaseAPI interface { // 返回: 配置值,是否存在。 GetStringSliceConfig(app AppInfo, datamap string, key string) ([]string, bool) - // CreatePublicDatamap [不安全][需要master权限]创建公共数据表 + // UnsafelyCreatePublicDatamap [不安全][需要master权限]创建公共数据表 // 参数: // - app: 应用信息。 // - datamapId: 数据表名称。 @@ -485,15 +486,11 @@ type WindStandardDataBaseAPI interface { } type AppInfo struct { - Name string - Version string + AppKey AppKey Author string Description string - Namespace string Homepage string License string - AppType string - Rule string CmdMap map[string]Cmd MessageEventHandler func(msg MessageEventInfo) NoticeEventHandler func(msg NoticeEventInfo) @@ -533,13 +530,13 @@ type AppInfoOption func(ei *AppInfo) func WithName(name string) AppInfoOption { return func(ei *AppInfo) { - ei.Name = name + ei.AppKey.Name = name } } func WithVersion(version string) AppInfoOption { return func(ei *AppInfo) { - ei.Version = version + ei.AppKey.Version = version } } @@ -569,26 +566,28 @@ func WithLicense(license string) AppInfoOption { func WithAppType(appType string) AppInfoOption { return func(ei *AppInfo) { - ei.AppType = appType + ei.AppKey.Type = appType } } func WithRule(rule string) AppInfoOption { return func(ei *AppInfo) { - ei.Rule = fmt.Sprintf("rule_%s", rule) + ei.AppKey.Rule = fmt.Sprintf("rule_%s", rule) } } func NewApp(opts ...AppInfoOption) AppInfo { Ext := AppInfo{ - Name: "WSP", - Version: "v1.0.0", - Author: "WSP", + AppKey: AppKey{ + Name: "WSP", + Version: "v1.0.0", + Type: "fun", + Rule: "none", + }, + Author: "WIND", Description: "A simple and easy-to-use bot framework", Homepage: "https://github.com/Sheyiyuan/wind_app_model", License: "MIT", - AppType: "fun", - Rule: "none", CmdMap: make(map[string]Cmd), ScheduledTasks: map[string]ScheduledTaskInfo{}, API: map[string]interface{}{}, @@ -601,10 +600,10 @@ func NewApp(opts ...AppInfoOption) AppInfo { func (ai *AppInfo) NewCmd(name string, description string, solve func(args []string, msg MessageEventInfo)) Cmd { return Cmd{ - Name: name, - Desc: description, - Solve: solve, - Rule: ai.Rule, + Name: name, + Desc: description, + Solve: solve, + AppKey: ai.AppKey, } } @@ -618,10 +617,10 @@ func (ai *AppInfo) NewScheduledTask(name string, description string, cron string } type Cmd struct { - Name string - Desc string - Solve func(args []string, msg MessageEventInfo) - Rule string + Name string + Desc string + Solve func(args []string, msg MessageEventInfo) + AppKey AppKey } type MessageEventInfo struct { @@ -709,20 +708,22 @@ type MessageInfo struct { } type MessageDataInfo struct { - Type string `json:"type,omitempty"` - Text string `json:"text,omitempty"` - Id string `json:"id,omitempty"` - File string `json:"file,omitempty"` - Url string `json:"url,omitempty"` - Magic string `json:"magic,omitempty"` - Qq string `json:"qq,omitempty"` - Title string `json:"title,omitempty"` - Content string `json:"content,omitempty"` - Image string `json:"image,omitempty"` - Audio string `json:"audio,omitempty"` - Lat string `json:"lat,omitempty"` - Lon string `json:"lon,omitempty"` - Data string `json:"data,omitempty"` + Type string `json:"type,omitempty"` + Text string `json:"text,omitempty"` + Id string `json:"id,omitempty"` + File string `json:"file,omitempty"` + Url string `json:"url,omitempty"` + Magic string `json:"magic,omitempty"` + Qq string `json:"qq,omitempty"` + Title string `json:"title,omitempty"` + Content any `json:"content,omitempty"` // Content string or []MessageDataInfo + Image string `json:"image,omitempty"` + Audio string `json:"audio,omitempty"` + Lat string `json:"lat,omitempty"` + Lon string `json:"lon,omitempty"` + Data string `json:"data,omitempty"` + UserId int64 `json:"user_id,omitempty"` + Nickname string `json:"name,omitempty"` } type ParamsInfo struct { @@ -864,3 +865,39 @@ type ScheduledTaskInfo struct { var WSP WindStandardProtocolAPI var WSD WindStandardDataBaseAPI + +type AppKey struct { + Name string `json:"name"` + Type string `json:"type"` + Level int32 `json:"level"` + Version string `json:"version"` + Rule string `json:"rule"` +} + +func (msg *MessageEventInfo) GetAt() []string { + var at []string + for _, v := range msg.Message { + if v.Type == "at" { + at = append(at, v.Data.Qq) + } + } + return at +} + +func (msg *MessageEventInfo) GetText() string { + var text string + for _, v := range msg.Message { + if v.Type == "text" { + text += v.Data.Text + } + } + return text +} + +func (msg *MessageEventInfo) JsonMarshal() string { + jsonData, err := json.Marshal(msg) + if err != nil { + return "" + } + return string(jsonData) +}