From c2382c8a31589507d2a13d03d338ef1eda555aa8 Mon Sep 17 00:00:00 2001 From: Sheyiyuan <2125107118@qq.com> Date: Wed, 26 Feb 2025 09:07:39 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86js=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=9F=BA=E7=A1=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LOG/log.go | 22 ++++--- core/app_admin_unix.go | 143 +++++++++++++++++++++++++++++++++++++++++ core/cron.go | 6 +- go.mod | 5 ++ go.sum | 14 ++++ wba/wind.go | 63 +++++++++--------- 6 files changed, 209 insertions(+), 44 deletions(-) diff --git a/LOG/log.go b/LOG/log.go index e92c112..074c6d5 100644 --- a/LOG/log.go +++ b/LOG/log.go @@ -10,16 +10,20 @@ import ( ) func Trace(text string, msg ...interface{}) { - pc, file, line, ok := runtime.Caller(3) - if !ok { - pc, file, line, ok = runtime.Caller(2) - } - if ok { - funcName := runtime.FuncForPC(pc).Name() - log.Printf("[Trace] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...)) - } else { - log.Printf("[Trace] %s\n", fmt.Sprintf(text, msg...)) + var pc uintptr + var file string + var line int + var ok bool + // 从第 2 层开始循环查找调用栈,直到找不到调用信息为止 + for i := 2; ; i++ { + pc, file, line, ok = runtime.Caller(i) + if !ok || line <= 0 { + pc, file, line, ok = runtime.Caller(i - 1) + break + } } + funcName := runtime.FuncForPC(pc).Name() + log.Printf("[Trace] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...)) } func Debug(text string, msg ...interface{}) { diff --git a/core/app_admin_unix.go b/core/app_admin_unix.go index cf80fb5..d7bd4fd 100644 --- a/core/app_admin_unix.go +++ b/core/app_admin_unix.go @@ -6,6 +6,7 @@ package core import ( "ProjectWIND/LOG" "ProjectWIND/wba" + "github.com/dop251/goja" "os" "path/filepath" "plugin" @@ -58,9 +59,151 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i } CmdMap = mergeMaps(CmdMap, app.Get().CmdMap) + ScheduledTasks := app.Get().ScheduledTasks + for _, task := range ScheduledTasks { + RegisterCron(app.Get().Name, task) + } LOG.Info("应用 %s 加载成功", pluginPath) return 1, 1 + } + if ext == ".js" { + pluginPath := filepath.Join(appsDir, file.Name()) + jsCode, err := os.ReadFile(pluginPath) + if err != nil { + LOG.Error("读取应用 %s 时发生错误: %v", pluginPath, err) + return 1, 0 + } + runtime := goja.New() + _, err = runtime.RunString(string(jsCode)) + if err != nil { + LOG.Error("执行应用 %s 失败: %v", pluginPath, err) + return 1, 0 + } + + // 创建JS可用的wbaObj对象 + wbaObj := runtime.NewObject() + wsp := runtime.NewObject() + _ = runtime.Set("wba", wbaObj) + _ = wbaObj.Set("NewApp", wba.NewApp) + _ = wbaObj.Set("NewCmd", wba.NewCmd) + _ = wbaObj.Set("NewScheduledTask", wba.NewScheduledTask) + _ = wbaObj.Set("WithName", wba.WithName) + _ = wbaObj.Set("WithAuthor", wba.WithAuthor) + _ = wbaObj.Set("WithVersion", wba.WithVersion) + _ = wbaObj.Set("WithDescription", wba.WithDescription) + _ = wbaObj.Set("WithWebUrl", wba.WithWebUrl) + _ = wbaObj.Set("WithLicense", wba.WithLicense) + _ = wbaObj.Set("WithAppType", wba.WithAppType) + _ = wbaObj.Set("WithRule", wba.WithRule) + _ = wbaObj.Set("WSP", wsp) + _ = wsp.Set("UnsafelySendMsg", AppApi.UnsafelySendMsg) + _ = wsp.Set("UnsafelySendPrivateMsg", AppApi.UnsafelySendPrivateMsg) + _ = wsp.Set("UnsafelySendGroupMsg", AppApi.UnsafelySendGroupMsg) + _ = wsp.Set("SendMsg", AppApi.SendMsg) + _ = wsp.Set("SendPrivateMsg", AppApi.SendPrivateMsg) + _ = wsp.Set("SendGroupMsg", AppApi.SendGroupMsg) + _ = wsp.Set("UnsafelyDeleteMsg", AppApi.UnsafelyDeleteMsg) + _ = wsp.Set("DeleteMsg", AppApi.DeleteMsg) + _ = wsp.Set("SendLike", AppApi.SendLike) + _ = wsp.Set("SetGroupKick", AppApi.SetGroupKick) + _ = wsp.Set("SetGroupBan", AppApi.SetGroupBan) + _ = wsp.Set("SetGroupWholeBan", AppApi.SetGroupWholeBan) + _ = wsp.Set("SetGroupAdmin", AppApi.SetGroupAdmin) + _ = wsp.Set("SetGroupLeave", AppApi.SetGroupLeave) + _ = wsp.Set("SetGroupCard", AppApi.SetGroupCard) + _ = wsp.Set("SetGroupName", AppApi.SetGroupName) + _ = wsp.Set("SetGroupSpecialTitle", AppApi.SetGroupSpecialTitle) + _ = wsp.Set("SetFriendAddRequest", AppApi.SetFriendAddRequest) + _ = wsp.Set("SetGroupAddRequest", AppApi.SetGroupAddRequest) + _ = wsp.Set("GetLoginInfo", AppApi.GetLoginInfo) + _ = wsp.Set("GetVersionInfo", AppApi.GetVersionInfo) + _ = wsp.Set("GetMsg", AppApi.GetMsg) + _ = wsp.Set("GetGroupInfo", AppApi.GetGroupInfo) + _ = wsp.Set("GetForwardMsg", AppApi.GetForwardMsg) + _ = wsp.Set("GetStrangerInfo", AppApi.GetStrangerInfo) + _ = wsp.Set("GetGroupList", AppApi.GetGroupList) + _ = wsp.Set("GetGroupMemberList", AppApi.GetGroupMemberList) + _ = wsp.Set("GetFriendList", AppApi.GetFriendList) + _ = wsp.Set("GetGroupMemberInfo", AppApi.GetGroupMemberInfo) + _ = wsp.Set("GetGroupHonorInfo", AppApi.GetGroupHonorInfo) + _ = wsp.Set("GetStatus", AppApi.GetStatus) + _ = wsp.Set("GetCookies", AppApi.GetCookies) + _ = wsp.Set("GetCSRFToken", AppApi.GetCSRFToken) + _ = wsp.Set("GetCredentials", AppApi.GetCredentials) + _ = wsp.Set("GetImage", AppApi.GetImage) + _ = wsp.Set("GetRecord", AppApi.GetRecord) + _ = wsp.Set("CanSendImage", AppApi.CanSendImage) + _ = 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) + + // 获取AppInit函数 + appInitVal := runtime.Get("AppInit") + if appInitVal == nil || goja.IsUndefined(appInitVal) { + LOG.Error("应用 %s 缺少AppInit函数", pluginPath) + return 1, 0 + } + appInitFunc, ok := goja.AssertFunction(appInitVal) + if !ok { + LOG.Error("应用 %s 的AppInit不是有效函数", pluginPath) + return 1, 0 + } + + // 调用AppInit获取应用实例 + jsApp, err := appInitFunc(goja.Undefined()) + if err != nil { + LOG.Error("初始化应用实例失败: %v", err) + return 1, 0 + } + + // 调用Init方法 + initVal := jsApp.ToObject(runtime).Get("Init") + initFunc, ok := goja.AssertFunction(initVal) + if !ok { + LOG.Error("应用 %s 缺少有效的Init方法", pluginPath) + return 1, 0 + } + + _, err = initFunc(wbaObj) + if err != nil { + LOG.Trace("应用初始化失败: %v", err) + return 1, 0 + } + + // 调用Get方法 + getVal := jsApp.ToObject(runtime).Get("Get") + getFunc, ok := goja.AssertFunction(getVal) + if !ok { + LOG.Error("应用 %s 缺少有效的Get方法", pluginPath) + return 1, 0 + } + + appInfoVal, err := getFunc(jsApp) + if err != nil { + LOG.Error("获取应用信息失败: %v", err) + return 1, 0 + } + + // 转换应用信息 + var appInfo wba.AppInfo + if err := runtime.ExportTo(appInfoVal, &appInfo); err != nil { + LOG.Error("应用信息转换失败: %v", err) + return 1, 0 + } + + // 合并命令 + CmdMap = mergeMaps(CmdMap, appInfo.CmdMap) + + // 注册定时任务 + for _, task := range appInfo.ScheduledTasks { + RegisterCron(appInfo.Name, task) + } + + LOG.Info("JS应用 %s 加载成功", pluginPath) + return 1, 1 } return 0, 0 } diff --git a/core/cron.go b/core/cron.go index 2f76b45..7f3fa8d 100644 --- a/core/cron.go +++ b/core/cron.go @@ -6,13 +6,13 @@ import ( "github.com/robfig/cron/v3" ) -func RegisterCron(task wba.ScheduledTaskInfo) { +func RegisterCron(appNames string, task wba.ScheduledTaskInfo) { // 注册定时任务 c := cron.New(cron.WithSeconds()) _, err := c.AddFunc(task.Cron, task.Task) if err != nil { - LOG.Error("添加定时任务 %s 时出错%v:", task.Name, err) + LOG.Error("添加定时任务 [%s]%s 时出错%v:", appNames, task.Name, err) } c.Start() - LOG.Info("定时任务 %s 注册成功", task.Name) + LOG.Info("定时任务 [%s]%s 注册成功", appNames, task.Name) } diff --git a/go.mod b/go.mod index 0ba0ac9..8e03792 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.23.2 require ( github.com/cloudwego/hertz v0.9.5 + github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17 github.com/gorilla/websocket v1.5.3 github.com/robfig/cron/v3 v3.0.1 ) @@ -15,8 +16,11 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cloudwego/netpoll v0.6.4 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/golang/protobuf v1.5.0 // indirect + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/nyaruka/phonenumbers v1.0.55 // indirect github.com/tidwall/gjson v1.14.4 // indirect @@ -25,5 +29,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.3.8 // indirect google.golang.org/protobuf v1.27.1 // indirect ) diff --git a/go.sum b/go.sum index 37ed100..3d95bf6 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/bytedance/gopkg v0.1.0 h1:aAxB7mm1qms4Wz4sp8e1AtKDOeFLtdqvGiUe7aonRJs= github.com/bytedance/gopkg v0.1.0/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= github.com/bytedance/mockey v1.2.12 h1:aeszOmGw8CPX8CRx1DZ/Glzb1yXvhjDh6jdFBNZjsU4= @@ -18,13 +20,21 @@ github.com/cloudwego/netpoll v0.6.4/go.mod h1:BtM+GjKTdwKoC8IOzD08/+8eEn2gYoiNLi github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17 h1:spJaibPy2sZNwo6Q0HjBVufq7hBUj5jNFOKRoogCBow= +github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= @@ -72,6 +82,8 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -79,6 +91,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/wba/wind.go b/wba/wind.go index 8bcb5e9..cdfab18 100644 --- a/wba/wind.go +++ b/wba/wind.go @@ -266,8 +266,8 @@ type WindStandardProtocolAPI interface { } type DataBaseHandler interface { - Set(appName string, datamap string, unit string, id string, key string, value interface{}) - Get(appName string, datamap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool) + Set(appName string, dataMap string, unit string, id string, key string, value interface{}) + Get(appName string, dataMap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool) } type AppInfo struct { @@ -307,16 +307,20 @@ func (ai *AppInfo) AddNoticeEventHandler(ScheduledTask ScheduledTaskInfo) { ai.ScheduledTasks[ScheduledTask.Name] = ScheduledTask } -func (ai *AppInfo) VarSet(datamap string, unit string, id string, key string, value string) { +func (ai *AppInfo) AddScheduledTask(task ScheduledTaskInfo) { + ai.ScheduledTasks[task.Name] = task +} + +func (ai *AppInfo) VarSet(dataMap string, unit string, id string, key string, value string) { if ai.dbHandler != nil { - ai.dbHandler.Set(ai.Name, datamap, unit, id, key, value) + ai.dbHandler.Set(ai.Name, dataMap, unit, id, key, value) } } // VarGet 获取变量 -func (ai *AppInfo) VarGet(datamap string, unit string, id string, key string) (string, bool) { +func (ai *AppInfo) VarGet(dataMap string, unit string, id string, key string) (string, bool) { if ai.dbHandler != nil { - res, ok := ai.dbHandler.Get(ai.Name, datamap, unit, id, key, false) + res, ok := ai.dbHandler.Get(ai.Name, dataMap, unit, id, key, false) if !ok { return "", false } @@ -330,9 +334,9 @@ func (ai *AppInfo) VarGet(datamap string, unit string, id string, key string) (s } // GetIntConfig 获取整数配置 -func (ai *AppInfo) GetIntConfig(datamap string, key string) (int64, bool) { +func (ai *AppInfo) GetIntConfig(dataMap string, key string) (int64, bool) { if ai.dbHandler != nil { - res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "number", key, true) + res, ok := ai.dbHandler.Get(ai.Name, dataMap, "config", "number", key, true) if !ok { return 0, false } @@ -346,9 +350,9 @@ func (ai *AppInfo) GetIntConfig(datamap string, key string) (int64, bool) { } // GetStringConfig 获取字符串配置 -func (ai *AppInfo) GetStringConfig(datamap string, key string) (string, bool) { +func (ai *AppInfo) GetStringConfig(dataMap string, key string) (string, bool) { if ai.dbHandler != nil { - res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "string", key, true) + res, ok := ai.dbHandler.Get(ai.Name, dataMap, "config", "string", key, true) if !ok { return "", false } @@ -362,9 +366,9 @@ func (ai *AppInfo) GetStringConfig(datamap string, key string) (string, bool) { } // GetFloatConfig 获取浮点数配置 -func (ai *AppInfo) GetFloatConfig(datamap string, key string) (float64, bool) { +func (ai *AppInfo) GetFloatConfig(dataMap string, key string) (float64, bool) { if ai.dbHandler != nil { - res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "float", key, true) + res, ok := ai.dbHandler.Get(ai.Name, dataMap, "config", "float", key, true) if !ok { return 0, false } @@ -378,9 +382,9 @@ func (ai *AppInfo) GetFloatConfig(datamap string, key string) (float64, bool) { } // GetIntSliceConfig 获取整数切片配置 -func (ai *AppInfo) GetIntSliceConfig(datamap string, key string) ([]int64, bool) { +func (ai *AppInfo) GetIntSliceConfig(dataMap string, key string) ([]int64, bool) { if ai.dbHandler != nil { - res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "number_slice", key, true) + res, ok := ai.dbHandler.Get(ai.Name, dataMap, "config", "number_slice", key, true) if !ok { return nil, false } @@ -394,9 +398,9 @@ func (ai *AppInfo) GetIntSliceConfig(datamap string, key string) ([]int64, bool) } // GetStringSliceConfig 获取字符串切片配置 -func (ai *AppInfo) GetStringSliceConfig(datamap string, key string) ([]string, bool) { +func (ai *AppInfo) GetStringSliceConfig(dataMap string, key string) ([]string, bool) { if ai.dbHandler != nil { - res, ok := ai.dbHandler.Get(ai.Name, datamap, "config", "string_slice", key, true) + res, ok := ai.dbHandler.Get(ai.Name, dataMap, "config", "string_slice", key, true) if !ok { return nil, false } @@ -435,12 +439,6 @@ func WithDescription(description string) AppInfoOption { } } -func WithNamespace(namespace string) AppInfoOption { - return func(ei *AppInfo) { - ei.Namespace = namespace - } -} - func WithWebUrl(webUrl string) AppInfoOption { return func(ei *AppInfo) { ei.Homepage = webUrl @@ -467,16 +465,17 @@ func WithRule(rule string) AppInfoOption { func NewApp(opts ...AppInfoOption) AppInfo { Ext := AppInfo{ - Name: "WSP", - Version: "v1.0.0", - Author: "WSP", - Description: "A simple and easy-to-use bot framework", - Namespace: "PUBLIC", - Homepage: "https://github.com/Sheyiyuan/wind_app_model", - License: "MIT", - AppType: "fun", - Rule: "none", - CmdMap: make(map[string]Cmd), + Name: "WSP", + Version: "v1.0.0", + Author: "WSP", + 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{}{}, } for _, opt := range opts { opt(&Ext) From 753c6dc0cafe2293d10c44cc4eae4ac72abaffb4 Mon Sep 17 00:00:00 2001 From: Sheyiyuan <2125107118@qq.com> Date: Wed, 26 Feb 2025 09:10:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=B8=BAWindows=E6=B7=BB=E5=8A=A0=E4=BA=86?= =?UTF-8?q?js=E6=8F=92=E4=BB=B6=E5=9F=BA=E7=A1=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/app_admin_windows.go | 139 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/core/app_admin_windows.go b/core/app_admin_windows.go index 837afd5..258d0db 100644 --- a/core/app_admin_windows.go +++ b/core/app_admin_windows.go @@ -76,6 +76,145 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i return 1, 1 } + if ext == ".js" { + pluginPath := filepath.Join(appsDir, file.Name()) + jsCode, err := os.ReadFile(pluginPath) + if err != nil { + LOG.Error("读取应用 %s 时发生错误: %v", pluginPath, err) + return 1, 0 + } + + runtime := goja.New() + _, err = runtime.RunString(string(jsCode)) + if err != nil { + LOG.Error("执行应用 %s 失败: %v", pluginPath, err) + return 1, 0 + } + + // 创建JS可用的wbaObj对象 + wbaObj := runtime.NewObject() + wsp := runtime.NewObject() + _ = runtime.Set("wba", wbaObj) + _ = wbaObj.Set("NewApp", wba.NewApp) + _ = wbaObj.Set("NewCmd", wba.NewCmd) + _ = wbaObj.Set("NewScheduledTask", wba.NewScheduledTask) + _ = wbaObj.Set("WithName", wba.WithName) + _ = wbaObj.Set("WithAuthor", wba.WithAuthor) + _ = wbaObj.Set("WithVersion", wba.WithVersion) + _ = wbaObj.Set("WithDescription", wba.WithDescription) + _ = wbaObj.Set("WithWebUrl", wba.WithWebUrl) + _ = wbaObj.Set("WithLicense", wba.WithLicense) + _ = wbaObj.Set("WithAppType", wba.WithAppType) + _ = wbaObj.Set("WithRule", wba.WithRule) + _ = wbaObj.Set("WSP", wsp) + _ = wsp.Set("UnsafelySendMsg", AppApi.UnsafelySendMsg) + _ = wsp.Set("UnsafelySendPrivateMsg", AppApi.UnsafelySendPrivateMsg) + _ = wsp.Set("UnsafelySendGroupMsg", AppApi.UnsafelySendGroupMsg) + _ = wsp.Set("SendMsg", AppApi.SendMsg) + _ = wsp.Set("SendPrivateMsg", AppApi.SendPrivateMsg) + _ = wsp.Set("SendGroupMsg", AppApi.SendGroupMsg) + _ = wsp.Set("UnsafelyDeleteMsg", AppApi.UnsafelyDeleteMsg) + _ = wsp.Set("DeleteMsg", AppApi.DeleteMsg) + _ = wsp.Set("SendLike", AppApi.SendLike) + _ = wsp.Set("SetGroupKick", AppApi.SetGroupKick) + _ = wsp.Set("SetGroupBan", AppApi.SetGroupBan) + _ = wsp.Set("SetGroupWholeBan", AppApi.SetGroupWholeBan) + _ = wsp.Set("SetGroupAdmin", AppApi.SetGroupAdmin) + _ = wsp.Set("SetGroupLeave", AppApi.SetGroupLeave) + _ = wsp.Set("SetGroupCard", AppApi.SetGroupCard) + _ = wsp.Set("SetGroupName", AppApi.SetGroupName) + _ = wsp.Set("SetGroupSpecialTitle", AppApi.SetGroupSpecialTitle) + _ = wsp.Set("SetFriendAddRequest", AppApi.SetFriendAddRequest) + _ = wsp.Set("SetGroupAddRequest", AppApi.SetGroupAddRequest) + _ = wsp.Set("GetLoginInfo", AppApi.GetLoginInfo) + _ = wsp.Set("GetVersionInfo", AppApi.GetVersionInfo) + _ = wsp.Set("GetMsg", AppApi.GetMsg) + _ = wsp.Set("GetGroupInfo", AppApi.GetGroupInfo) + _ = wsp.Set("GetForwardMsg", AppApi.GetForwardMsg) + _ = wsp.Set("GetStrangerInfo", AppApi.GetStrangerInfo) + _ = wsp.Set("GetGroupList", AppApi.GetGroupList) + _ = wsp.Set("GetGroupMemberList", AppApi.GetGroupMemberList) + _ = wsp.Set("GetFriendList", AppApi.GetFriendList) + _ = wsp.Set("GetGroupMemberInfo", AppApi.GetGroupMemberInfo) + _ = wsp.Set("GetGroupHonorInfo", AppApi.GetGroupHonorInfo) + _ = wsp.Set("GetStatus", AppApi.GetStatus) + _ = wsp.Set("GetCookies", AppApi.GetCookies) + _ = wsp.Set("GetCSRFToken", AppApi.GetCSRFToken) + _ = wsp.Set("GetCredentials", AppApi.GetCredentials) + _ = wsp.Set("GetImage", AppApi.GetImage) + _ = wsp.Set("GetRecord", AppApi.GetRecord) + _ = wsp.Set("CanSendImage", AppApi.CanSendImage) + _ = 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) + + // 获取AppInit函数 + appInitVal := runtime.Get("AppInit") + if appInitVal == nil || goja.IsUndefined(appInitVal) { + LOG.Error("应用 %s 缺少AppInit函数", pluginPath) + return 1, 0 + } + appInitFunc, ok := goja.AssertFunction(appInitVal) + if !ok { + LOG.Error("应用 %s 的AppInit不是有效函数", pluginPath) + return 1, 0 + } + + // 调用AppInit获取应用实例 + jsApp, err := appInitFunc(goja.Undefined()) + if err != nil { + LOG.Error("初始化应用实例失败: %v", err) + return 1, 0 + } + + // 调用Init方法 + initVal := jsApp.ToObject(runtime).Get("Init") + initFunc, ok := goja.AssertFunction(initVal) + if !ok { + LOG.Error("应用 %s 缺少有效的Init方法", pluginPath) + return 1, 0 + } + + _, err = initFunc(wbaObj) + if err != nil { + LOG.Trace("应用初始化失败: %v", err) + return 1, 0 + } + + // 调用Get方法 + getVal := jsApp.ToObject(runtime).Get("Get") + getFunc, ok := goja.AssertFunction(getVal) + if !ok { + LOG.Error("应用 %s 缺少有效的Get方法", pluginPath) + return 1, 0 + } + + appInfoVal, err := getFunc(jsApp) + if err != nil { + LOG.Error("获取应用信息失败: %v", err) + return 1, 0 + } + + // 转换应用信息 + var appInfo wba.AppInfo + if err := runtime.ExportTo(appInfoVal, &appInfo); err != nil { + LOG.Error("应用信息转换失败: %v", err) + return 1, 0 + } + + // 合并命令 + CmdMap = mergeMaps(CmdMap, appInfo.CmdMap) + + // 注册定时任务 + for _, task := range appInfo.ScheduledTasks { + RegisterCron(appInfo.Name, task) + } + + LOG.Info("JS应用 %s 加载成功", pluginPath) + return 1, 1 + } return 0, 0 }