Merge remote-tracking branch 'origin/dev' into bhc

This commit is contained in:
Thun_Ann 2025-03-03 16:03:31 +08:00
commit 4cc3808ab8
13 changed files with 369 additions and 544 deletions

View File

@ -2,6 +2,7 @@ package core
import (
"ProjectWIND/LOG"
"ProjectWIND/database"
"ProjectWIND/wba"
"crypto/rand"
"fmt"
@ -818,7 +819,133 @@ func (a *apiInfo) Log(content string, args ...interface{}) {
}
//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)
}
func (dbi *databaseInfo) SetUserVariable(app wba.AppInfo, id string, key string, value string) {
dbi.varSet(app, app.Name, "user", id, key, value)
}
func (dbi *databaseInfo) SetGroupVariable(app wba.AppInfo, id string, key string, value string) {
dbi.varSet(app, app.Name, "group", id, key, value)
}
func (dbi *databaseInfo) SetGlobalVariable(app wba.AppInfo, id string, key string, value string) {
dbi.varSet(app, app.Name, "global", id, key, value)
}
func (dbi *databaseInfo) SetOutUserVariable(app wba.AppInfo, datamap string, id string, key string, value string) {
dbi.varSet(app, datamap, "user", id, key, value)
}
func (dbi *databaseInfo) SetOutGroupVariable(app wba.AppInfo, datamap string, id string, key string, value string) {
dbi.varSet(app, datamap, "group", id, key, value)
}
func (dbi *databaseInfo) SetOutGlobalVariable(app wba.AppInfo, datamap string, id string, key string, value string) {
dbi.varSet(app, datamap, "global", id, key, value)
}
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)
if !ok {
return "", false
}
resStr, ok := res.(string)
if !ok {
return "", false
}
return resStr, true
}
func (dbi *databaseInfo) GetUserVariable(app wba.AppInfo, id string, key string) (string, bool) {
return dbi.varGet(app, app.Name, "user", id, key)
}
func (dbi *databaseInfo) GetGroupVariable(app wba.AppInfo, id string, key string) (string, bool) {
return dbi.varGet(app, app.Name, "group", id, key)
}
func (dbi *databaseInfo) GetGlobalVariable(app wba.AppInfo, id string, key string) (string, bool) {
return dbi.varGet(app, app.Name, "global", id, key)
}
func (dbi *databaseInfo) GetOutUserVariable(app wba.AppInfo, datamap string, id string, key string) (string, bool) {
return dbi.varGet(app, datamap, "user", id, key)
}
func (dbi *databaseInfo) GetOutGroupVariable(app wba.AppInfo, datamap string, id string, key string) (string, bool) {
return dbi.varGet(app, datamap, "group", id, key)
}
func (dbi *databaseInfo) GetOutGlobalVariable(app wba.AppInfo, datamap string, id string, key string) (string, bool) {
return dbi.varGet(app, datamap, "global", id, key)
}
func (dbi *databaseInfo) GetIntConfig(app wba.AppInfo, datamap string, key string) (int64, bool) {
res, ok := database.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 (dbi *databaseInfo) GetStringConfig(app wba.AppInfo, datamap string, key string) (string, bool) {
res, ok := database.Get(app.Name, datamap, "config", "string", key, true)
if !ok {
return "", false
}
resStr, ok := res.(string)
if !ok {
return "", false
}
return resStr, true
}
func (dbi *databaseInfo) GetFloatConfig(app wba.AppInfo, datamap string, key string) (float64, bool) {
res, ok := database.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 (dbi *databaseInfo) GetIntSliceConfig(app wba.AppInfo, datamap string, key string) ([]int64, bool) {
res, ok := database.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 (dbi *databaseInfo) GetStringSliceConfig(app wba.AppInfo, datamap string, key string) ([]string, bool) {
res, ok := database.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
}
// 文件管理模块
//TODO: 文件管理模块待实现
@ -829,6 +956,7 @@ func (a *apiInfo) Log(content string, args ...interface{}) {
//核心信息调用模块
var AppApi apiInfo
var DatabaseApi databaseInfo
func GenerateUUID() (string, error) {
uuid := make([]byte, 16)

View File

@ -1,20 +1,22 @@
//go:build linux || darwin
// +build linux darwin
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/typed"
"ProjectWIND/wba"
"github.com/dop251/goja"
"os"
"path/filepath"
"plugin"
"strings"
)
var CmdMap = make(map[string]wba.Cmd)
var CmdMap = make([]map[string]wba.Cmd, 4)
var AppMap = make(map[typed.AppKey]wba.AppInfo)
func ReloadApps() (total int, success int) {
// 清空AppMap和CmdMap
CmdMap = make([]map[string]wba.Cmd, 4)
AppMap = make(map[typed.AppKey]wba.AppInfo)
appsDir := "./data/app/"
appFiles, err := os.ReadDir(appsDir)
total = 0
@ -29,7 +31,7 @@ func ReloadApps() (total int, success int) {
total += totalDelta
success += successDelta
}
CmdMap = mergeMaps(CmdMap, AppCore.CmdMap)
CmdMap[0] = AppCore.CmdMap
return total, success
}
@ -39,33 +41,6 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i
}
ext := filepath.Ext(file.Name())
if ext == ".so" {
pluginPath := filepath.Join(appsDir, file.Name())
p, err := plugin.Open(pluginPath)
if err != nil {
LOG.Error("打开应用 %s 时发生错误: %v", pluginPath, err)
return 1, 0
}
AppInit, err := p.Lookup("AppInit")
if err != nil {
LOG.Error("找不到应用 %s 提供的 AppInit 接口: %v", pluginPath, err)
return 1, 0
}
app := AppInit.(func() wba.AppInfo)()
err = app.Init(&AppApi)
if err != nil {
LOG.Error("初始化应用 %s 失败: %v", pluginPath, err)
}
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)
@ -84,10 +59,9 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i
// 创建JS可用的wbaObj对象
wbaObj := runtime.NewObject()
wsp := runtime.NewObject()
wsd := 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)
@ -96,7 +70,8 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i
_ = wbaObj.Set("WithLicense", wba.WithLicense)
_ = wbaObj.Set("WithAppType", wba.WithAppType)
_ = wbaObj.Set("WithRule", wba.WithRule)
_ = wbaObj.Set("WSP", wsp)
_ = wbaObj.Set("wsp", wsp)
_ = wbaObj.Set("wsd", wsd)
_ = wsp.Set("UnsafelySendMsg", AppApi.UnsafelySendMsg)
_ = wsp.Set("UnsafelySendPrivateMsg", AppApi.UnsafelySendPrivateMsg)
_ = wsp.Set("UnsafelySendGroupMsg", AppApi.UnsafelySendGroupMsg)
@ -139,6 +114,23 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i
_ = wsp.Set("CleanCache", AppApi.CleanCache)
_ = wsp.Set("GetLoginInfo", AppApi.LogWith)
_ = wsp.Set("GetVersionInfo", AppApi.GetVersionInfo)
_ = wsd.Set("SetUserVariable", DatabaseApi.SetUserVariable)
_ = wsd.Set("SetGroupVariable", DatabaseApi.SetGroupVariable)
_ = wsd.Set("SetGlobalVariable", DatabaseApi.SetGlobalVariable)
_ = wsd.Set("SetOutUserVariable", DatabaseApi.SetOutUserVariable)
_ = wsd.Set("SetOutGroupVariable", DatabaseApi.SetOutGroupVariable)
_ = wsd.Set("SetOutGlobalVariable", DatabaseApi.SetOutGlobalVariable)
_ = wsd.Set("GetUserVariable", DatabaseApi.GetUserVariable)
_ = wsd.Set("GetGroupVariable", DatabaseApi.GetGroupVariable)
_ = wsd.Set("GetGlobalVariable", DatabaseApi.GetGlobalVariable)
_ = wsd.Set("GetOutUserVariable", DatabaseApi.GetOutUserVariable)
_ = wsd.Set("GetOutGroupVariable", DatabaseApi.GetOutGroupVariable)
_ = wsd.Set("GetOutGlobalVariable", DatabaseApi.GetOutGlobalVariable)
_ = wsd.Set("GetIntConfig", DatabaseApi.GetIntConfig)
_ = wsd.Set("GetFloatConfig", DatabaseApi.GetFloatConfig)
_ = wsd.Set("GetStringConfig", DatabaseApi.GetStringConfig)
_ = wsd.Set("GetIntSliceConfig", DatabaseApi.GetIntSliceConfig)
_ = wsd.Set("GetStringSliceConfig", DatabaseApi.GetStringSliceConfig)
// 获取AppInit函数
appInitVal := runtime.Get("AppInit")
@ -194,8 +186,10 @@ 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)
// 合并命令
CmdMap = mergeMaps(CmdMap, appInfo.CmdMap)
CmdMap[cmdIndex] = mergeMaps(CmdMap[cmdIndex], appInfo.CmdMap)
// 注册定时任务
for _, task := range appInfo.ScheduledTasks {
@ -219,3 +213,19 @@ func mergeMaps(map1, map2 map[string]wba.Cmd) map[string]wba.Cmd {
}
return map3
}
func AppTypeToInt(appType string) int32 {
appType = strings.ToLower(appType)
switch appType {
case "system":
return 1
case "rule":
return 2
default:
return 3
}
}
func checkAppLevel(appInfo wba.AppInfo) int32 {
return 0
}

View File

@ -1,231 +0,0 @@
//go:build windows
// +build windows
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"fmt"
"os"
"path/filepath"
"syscall"
"unsafe"
)
var CmdMap = make(map[string]wba.Cmd)
func ReloadApps() (total int, success int) {
appsDir := "./data/app/"
appFiles, err := os.ReadDir(appsDir)
total = 0
success = 0
if err != nil {
LOG.Error("加载应用所在目录失败:%v", err)
return
}
for _, file := range appFiles {
totalDelta, successDelta := reloadAPP(file, appsDir)
total += totalDelta
success += successDelta
}
CmdMap = mergeMaps(CmdMap, AppCore.CmdMap)
return total, success
}
func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta int) {
if file.IsDir() {
return 0, 0
}
ext := filepath.Ext(file.Name())
if ext == ".dll" {
pluginPath := filepath.Join(appsDir, file.Name())
lib, err := syscall.LoadLibrary(pluginPath)
if err != nil {
LOG.Error("加载应用 %s 失败: %v", pluginPath, err)
return 1, 0
}
defer func(handle syscall.Handle) {
err := syscall.FreeLibrary(handle)
if err != nil {
LOG.Error("释放应用 %s 时发生错误: %v", pluginPath, err)
}
}(lib)
// 获取函数地址
sym, err := syscall.GetProcAddress(lib, "AppInit")
if err != nil {
fmt.Println("找不到应用 %s 提供的 AppInit 接口: %v", err)
return 1, 0
}
// 定义函数类型
AppInitPtr := (*func() wba.AppInfo)(unsafe.Pointer(&sym))
AppInit := *AppInitPtr
app := AppInit()
err = app.Init(&AppApi)
if err != nil {
LOG.Error("初始化应用 %s 失败: %v", pluginPath, err)
}
CmdMap = mergeMaps(CmdMap, app.Get().CmdMap)
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
}
func mergeMaps(map1, map2 map[string]wba.Cmd) map[string]wba.Cmd {
// 合并map1和map2到map3中
map3 := make(map[string]wba.Cmd)
for key, value := range map1 {
map3[key] = value
}
for key, value := range map2 {
map3[key] = value
}
return map3
}

View File

@ -21,7 +21,7 @@ func (app *AppInfo) Run(cmd string, args []string, msg wba.MessageEventInfo) err
if !ok {
return errors.New("cmd not found")
}
app.CmdMap[cmd].SOLVE(args, msg)
app.CmdMap[cmd].Solve(args, msg)
return nil
}
@ -29,15 +29,19 @@ func (app *AppInfo) Init(Api wba.WindStandardProtocolAPI) error {
return nil
}
func (app *AppInfo) InitWSD(Api wba.WindStandardDataBaseAPI) error {
return nil
}
func (app *AppInfo) GetCmd() map[string]wba.Cmd {
return app.CmdMap
}
func NewCmd(name string, help string, solve func(args []string, msg wba.MessageEventInfo)) wba.Cmd {
return wba.Cmd{
NAME: name,
DESC: help,
SOLVE: solve,
Name: name,
Desc: help,
Solve: solve,
}
}

View File

@ -16,13 +16,16 @@ func HandleMessage(msgJson []byte) {
}
// 处理消息
LOG.Info("收到消息:(来自:%v-%v:%v-%v)%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, msg.RawMessage)
//如果消息文本内容为bot发送框架信息。
cmd, args := CmdSplit(msg)
_, ok := CmdMap[cmd]
if ok {
LOG.Debug("执行命令:%v %v", cmd, args)
CmdMap[cmd].SOLVE(args, msg)
for _, cmdList := range CmdMap {
_, ok := cmdList[cmd]
if ok {
LOG.Debug("执行命令:%v %v", cmd, args)
cmdList[cmd].Solve(args, msg)
break
}
}
// TODO: 处理消息内容
}
@ -62,15 +65,17 @@ func CmdSplit(msg wba.MessageEventInfo) (string, []string) {
return "", []string{}
}
}
//检查有无application.CmdList中的命令前缀
//检查有无application.CmdMap中的命令前缀
for _, prefix := range cmdPrefix {
if strings.HasPrefix(text, prefix) {
text = strings.TrimPrefix(text, prefix)
for cmd := range CmdMap {
if strings.HasPrefix(text, cmd) {
text = strings.TrimPrefix(text, cmd)
text = strings.TrimPrefix(text, " ")
return cmd, strings.Split(text, " ")
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, " ")
}
}
}
}

View File

@ -2,6 +2,7 @@ package core
import (
"ProjectWIND/LOG"
"ProjectWIND/typed"
"ProjectWIND/wba"
"encoding/json"
"fmt"
@ -10,16 +11,14 @@ import (
"net/url"
)
var gProtocolAddr string
var gToken string
var gProtocol typed.Protocol
// WebSocketHandler 接收WebSocket连接处的消息并处理
func WebSocketHandler(protocolAddr string, token string) error {
func WebSocketHandler(protocol typed.Protocol) error {
// 保存全局变量
gProtocolAddr = protocolAddr
gToken = token
gProtocol = protocol
// 解析连接URL
u, err := url.Parse(protocolAddr)
u, err := url.Parse(protocol.Addr)
if err != nil {
LOG.Error("Parse URL error: %v", err)
return err
@ -30,7 +29,7 @@ func WebSocketHandler(protocolAddr string, token string) error {
if err != nil {
LOG.Fatal("创建请求出错:%v", err)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Authorization", "Bearer "+protocol.Token)
// 配置WebSocket连接升级器
dialer := websocket.DefaultDialer
// 使用升级器建立WebSocket连接
@ -126,7 +125,7 @@ func wsAPI(body wba.APIRequestInfo) (Response wba.APIResponseInfo, err error) {
return wba.APIResponseInfo{}, err
}
// 解析连接URL
u, err := url.Parse(gProtocolAddr)
u, err := url.Parse(gProtocol.Addr)
if err != nil {
LOG.Error("Parse URL error: %v", err)
return wba.APIResponseInfo{}, err
@ -136,7 +135,7 @@ func wsAPI(body wba.APIRequestInfo) (Response wba.APIResponseInfo, err error) {
if err != nil {
LOG.Fatal("创建请求出错:%v", err)
}
req.Header.Set("Authorization", "Bearer "+gToken)
req.Header.Set("Authorization", "Bearer "+gProtocol.Token)
// 配置WebSocket连接升级器
dialer := websocket.DefaultDialer
// 使用升级器建立WebSocket连接

View File

@ -560,16 +560,6 @@ func Set(appName string, datamap string, unit string, id string, key string, val
dataSet(appName, unit, id, key, value, true, false)
}
type DatabaseHandlerImpl struct{}
func (dbh *DatabaseHandlerImpl) Set(appName string, datamap string, unit string, id string, key string, value interface{}) {
Set(appName, datamap, unit, id, key, value)
}
func (dbh *DatabaseHandlerImpl) Get(appName string, datamap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool) {
return Get(appName, datamap, unit, id, key, isGettingConfig)
}
// func VarSet(app wba.AppInfo, datamap string, unit string, id string, key string, value string) {
// Set(app.Name, datamap, unit, id, key, value)
// }

View File

@ -1 +0,0 @@
{"Id":"datamap","Users":{},"Groups":{},"Global":{}}

View File

@ -1,40 +0,0 @@
```go
func Start() //启动默认数据库
```
该函数用于启动数据库,加载数据,启动自动存储
Tips:需要异步启动数据库,否则进程会被阻塞
Tips:需要在启动数据库后等待其初始化建议使用time.Sleep()函数等待至少1秒
```go
func Get(database *database, category string, id string, key string) (string,bool) //获取变量,示例用法:// database.Get("user", "1001", "age") 表示查询db数据库中id为1001的用户个人变量age
```
该函数用于查询设定的变量
——category部分可以填入"user","group","global",分别表示个人变量,群变量,全局变量
——id为用户id或群id全局变量使用时id可以理解为命名空间
——key为要查询的变量名
返回值类型为string,bool第一个返回值为查询到的变量第二个返回值表示是否返回成功
```go
func Set(category string, id string, key string, value string) //修改变量,示例用法:
// database.Set("user", "1001", "age", "18") 表示将db数据库中id为1001的用户个人变量age设置为"18"
// 注意变量目前只支持string类型如果需要储存数据或对象请将它们转化为string类型再进行储存
// 该数据库的所有变量将会存放在/data/database/datamap.txt中请不要乱动这个文件
```
该函数用于新建或修改变量
——category部分可以填入"user","group","global",分别表示个人变量,群变量,全局变量
——id为用户id或群id全局变量使用时id可以理解为命名空间
——key为要修改的变量名
——value为要修改的变量值

View File

@ -14,8 +14,8 @@ TODO:
- ✅ 文件初始化
- ✅ 事件处理
- ✅ 数据库交互
- 插件系统
- 用户系统
- 插件系统
- 用户系统
- ❌ web ui
- ❌ 文档编写
@ -30,14 +30,19 @@ WIND全称WIND is not dice是一个基于 Go 语言开发的bot框架
## 1. goja
- **库名称**goja
- **仓库地址**[https://github.com/dop251/goja](https://github.com/dop251/goja)
- **用途说明**goja 作为一款强大的 JavaScript 解释器,在本项目中承担了处理动态的 JavaScript 脚本逻辑,为项目提供了灵活的脚本扩展能力,使得我们能够在 Go 项目中嵌入 JavaScript 代码来实现一些特定的业务规则处理等功能。它极大地丰富了项目的功能和灵活性,让我们能够更高效地开发出具有特色的功能模块
- **用途说明**goja 作为一款强大的 JavaScript 解释器,在本项目中承担了处理动态的 JavaScript 脚本逻辑,为项目提供了灵活的脚本扩展能力,使得我们能够在项目中实现js插件的功能。它极大地丰富了项目的功能和灵活性让跨平台的插件开发变得更加容易
## 2. gocron
- **库名称**gocron
- **仓库地址**[https://github.com/go-co-op/gocron](https://github.com/go-co-op/gocron)
- **用途说明**gocron 是一个出色的任务调度库。在本项目里,它被用于[详细的任务调度应用场景,比如定期执行数据同步任务、定时清理临时文件或缓存数据等],确保了项目中的各种定时任务能够精准、可靠地执行。其简洁易用的 API 设计大大降低了我们实现复杂任务调度逻辑的难度,为项目的稳定运行提供了有力保障。
- **用途说明**gocron 是一个出色的任务调度库。在本项目里,它被用作定时任务的调度器,确保了项目中的各种定时任务能够精准、可靠地执行。其简洁易用的 API 设计大大降低了我们实现复杂任务调度逻辑的难度,为项目的稳定运行提供了有力保障。
非常感谢 `goja``gocron` 项目团队的开源贡献,使得我们的项目开发能够借助这些优秀的工具快速推进,为用户带来更好的体验。
## 3. hertz
- **库名称**hertz
- **仓库地址**[https://github.com/cloudwego/hertz](https://github.com/cloudwego/hertz)
- **用途说明**hertz 是一个基于 Go 语言开发的高性能 HTTP 路由器。在本项目中,它被用作项目的 HTTP 服务器,为项目提供了快速、高效的 HTTP 请求处理能力。让我们能够灵活地对 HTTP 请求进行处理。
非常感谢以上项目团队的开源贡献,使得我们的项目开发能够借助这些优秀的工具快速推进,为用户带来更好的体验。
---
后面没有了,开发者很懒,什么都没写。

View File

@ -1,17 +1,34 @@
package typed
type CoreConfigInfo struct {
CoreName string `json:"core_name"`
Protocols []Protocol `json:"protocols"`
WebUIPort uint16 `json:"webui_port"`
PasswordHash string `json:"password_hash"`
ServiceName string `json:"service_name"`
CoreName string `json:"core_name"`
Protocol Protocol `json:"protocol"`
WebUIPort uint16 `json:"webui_port"`
PasswordHash string `json:"password_hash"`
ServiceName string `json:"service_name"`
}
type Protocol struct {
ProtocolName string `json:"protocol_name"`
ProtocolPlatform string `json:"protocol_platform"`
ProtocolAddr string `json:"protocol_addr"`
Token string `json:"token"`
Enable bool `json:"enable"`
Name string `json:"protocol_name"`
Platform string `json:"protocol_platform"`
Addr string `json:"protocol_addr"`
Token string `json:"token"`
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"`
}

100
utils.go
View File

@ -87,16 +87,16 @@ func checkAndUpdateConfig(configPath string) error {
var coreConfig typed.CoreConfigInfo
var defaultProtocol typed.Protocol
defaultProtocol.ProtocolName = "EXAMPLE"
defaultProtocol.ProtocolPlatform = "在这里输入协议平台"
defaultProtocol.ProtocolAddr = "在这里输入协议地址,如'ws://127.0.0.1:8080'"
defaultProtocol.Name = "EXAMPLE"
defaultProtocol.Platform = "在这里输入协议平台"
defaultProtocol.Addr = "在这里输入协议地址,如'ws://127.0.0.1:8080'"
defaultProtocol.Token = "在这里输入协议的Token"
defaultProtocol.Enable = true
var defaultConfig typed.CoreConfigInfo
defaultConfig.CoreName = "windCore"
defaultConfig.WebUIPort = 3211
defaultConfig.Protocols = []typed.Protocol{defaultProtocol}
defaultConfig.Protocol = defaultProtocol
defaultConfig.ServiceName = "wind"
// 读取配置文件
file, err := os.Open(configPath)
@ -120,11 +120,8 @@ func checkAndUpdateConfig(configPath string) error {
}
// 检查并更新配置
//if coreConfig.ProtocolAddr == "" {
// coreConfig.ProtocolAddr = defaultConfig.ProtocolAddr
//}
if coreConfig.Protocols == nil || len(coreConfig.Protocols) == 0 {
coreConfig.Protocols = defaultConfig.Protocols
if coreConfig.Protocol == (typed.Protocol{}) {
coreConfig.Protocol = defaultConfig.Protocol
}
if coreConfig.WebUIPort == 0 {
coreConfig.WebUIPort = defaultConfig.WebUIPort
@ -311,44 +308,30 @@ func startProtocol() {
LOG.Fatal("连接协议时,解析配置文件 ./data/core.json 失败: %v", err)
}
LOG.Info("正在启动WebSocket链接程序...")
protocolNum := 0
breakNum := 0
UnenableProtocolNum := 0
for _, protocol := range config.Protocols {
protocolName := protocol.ProtocolName
if protocolName == "EXAMPLE" {
continue
}
if protocolName == "" {
LOG.Warn("连接协议 %s 时,协议名称为空,跳过该协议", protocolName)
breakNum++
continue
}
//获取协议地址
protocolAddr := protocol.ProtocolAddr
if protocolAddr == "" {
LOG.Warn("连接协议 %s 时,协议地址为空,跳过该协议", protocolName)
breakNum++
continue
}
if protocol.Enable == false {
LOG.Warn("连接协议 %s 时,协议已禁用,跳过该协议", protocolName)
UnenableProtocolNum++
continue
}
//获取token
token := protocol.Token
// 启动 WebSocket 处理程序
go func() {
err := core.WebSocketHandler(protocolAddr, token)
if err != nil {
LOG.Error("连接协议时,启动 WebSocket 处理程序失败: %v", err)
}
}()
protocolNum++
protocol := config.Protocol
if protocol.Name == "EXAMPLE" {
LOG.Warn("未找到协议配置信息")
return
}
if protocol.Name == "" {
LOG.Warn("连接协议 %s 时,协议名称为空", protocol.Name)
return
}
//获取协议地址
protocolAddr := protocol.Addr
if protocolAddr == "" {
LOG.Warn("连接协议 %s 时,协议地址为空", protocol.Name)
return
}
if protocol.Enable == false {
LOG.Warn("连接协议 %s 时,协议已禁用", protocol.Name)
return
}
// 启动 WebSocket 处理程序
err = core.WebSocketHandler(protocol)
if err != nil {
LOG.Error("连接协议时 %s启动 WebSocket 处理程序失败: %v", protocol.Name, err)
}
LOG.Info(" %d 个协议服务启动完成, %d 个协议服务已禁用, %d 个协议服务因为配置错误被跳过。", protocolNum, UnenableProtocolNum, breakNum)
select {}
}
func ReloadApps() {
@ -360,30 +343,5 @@ func ReloadApps() {
func startDatabase() {
go database.Start()
time.Sleep(time.Second * 1)
// 读写测试
// for i := 0; i < 10; i++ {
// data, ok := database.Get("user", "test", "test"+fmt.Sprintf("%d", i))
// if !ok {
// LOG.Error("Failed to get data from database")
// continue
// }
// LOG.Info("Get data from database: %v", data)
// time.Sleep(time.Second * 1)
// }
// time.Sleep(time.Second * 1)
// for i := 0; i < 10; i++ {
// database.Set("user", "test", "test"+fmt.Sprintf("%d", i), "test"+fmt.Sprintf("%d", 1000+i))
// time.Sleep(time.Second * 1)
// }
// time.Sleep(time.Second * 1)
// for i := 0; i < 10; i++ {
// data, ok := database.Get("user", "test", "test"+fmt.Sprintf("%d", i))
// if !ok {
// LOG.Error("Failed to get data from database")
// continue
// }
// LOG.Info("Get data from database: %v", data)
// time.Sleep(time.Second * 1)
// }
select {}
}

View File

@ -7,6 +7,7 @@ import (
type APP interface {
Get() AppInfo
Init(api WindStandardProtocolAPI) error
InitWSD(api WindStandardDataBaseAPI) error
}
// WindStandardProtocolAPI Wind标准协议API,提供了onebot11标准中的API接口。
@ -265,9 +266,84 @@ type WindStandardProtocolAPI interface {
Log(log string, args ...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)
type WindStandardDataBaseAPI interface {
// SetUserVariable 设置用户变量
// SetGroupVariable 设置群组变量
// SetGlobalVariable 设置全局变量
// SetOutUserVarialbe 设置其他数据库中的用户变量(需要权限)
// SetOutGroupVarialbe 设置其他数据库中的群组变量(需要权限)
// SetOutGlobalVarialbe 设置其他数据库中的全局变量(需要权限)
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
// - datamap: 数据表名称。
SetUserVariable(app AppInfo, id string, key string, value string)
SetGroupVariable(app AppInfo, id string, key string, value string)
SetGlobalVariable(app AppInfo, id string, key string, value string)
SetOutUserVariable(app AppInfo, datamap string, id string, key string, value string)
SetOutGroupVariable(app AppInfo, datamap string, id string, key string, value string)
SetOutGlobalVariable(app AppInfo, datamap string, id string, key string, value string)
// GetUserVariable 获取用户变量
// GetGroupVariable 获取群组变量
// GetGlobalVariable 获取全局变量
// GetOutUserVariable 获取其他数据库中的用户变量(需要权限)
// GetOutGroupVariable 获取其他数据库中的群组变量(需要权限)
// GetOutGlobalVariable 获取其他数据库中的全局变量(需要权限)
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - datamap数据表名称。
// 返回: 变量值,是否存在。
GetUserVariable(app AppInfo, id string, key string) (string, bool)
GetGroupVariable(app AppInfo, id string, key string) (string, bool)
GetGlobalVariable(app AppInfo, id string, key string) (string, bool)
GetOutUserVariable(app AppInfo, datamap string, id string, key string) (string, bool)
GetOutGroupVariable(app AppInfo, datamap string, id string, key string) (string, bool)
GetOutGlobalVariable(app AppInfo, datamap string, id string, key string) (string, bool)
// GetIntConfig 获取指定数据单元的整数型配置。
// 参数:
// - app: 应用信息。
// - datamap: 数据单元名称。
// - key: 配置名称。
// 返回: 配置值,是否存在。
GetIntConfig(app AppInfo, datamap string, key string) (int64, bool)
// GetStringConfig 获取指定数据单元的字符串型配置。
// 参数:
// - app: 应用信息。
// - datamap: 数据单元名称。
// - key: 配置名称。
// 返回: 配置值,是否存在。
GetStringConfig(app AppInfo, datamap string, key string) (string, bool)
// GetFloatConfig 获取指定数据单元的浮点型配置。
// 参数:
// - app: 应用信息。
// - datamap: 数据单元名称。
// - key: 配置名称。
// 返回: 配置值,是否存在。
GetFloatConfig(app AppInfo, datamap string, key string) (float64, bool)
// GetIntSliceConfig 获取指定数据单元的整数型切片配置。
// 参数:
// - app: 应用信息。
// - datamap: 数据单元名称。
// - key: 配置名称。
// 返回: 配置值,是否存在。
GetIntSliceConfig(app AppInfo, datamap string, key string) ([]int64, bool)
// GetStringSliceConfig 获取指定数据单元的字符串型切片配置。
// 参数:
// - app: 应用信息。
// - datamap: 数据单元名称。
// - key: 配置名称。
// 返回: 配置值,是否存在。
GetStringSliceConfig(app AppInfo, datamap string, key string) ([]string, bool)
}
type AppInfo struct {
@ -287,7 +363,6 @@ type AppInfo struct {
MetaEventHandler func(msg MetaEventInfo)
ScheduledTasks map[string]ScheduledTaskInfo
API map[string]interface{}
dbHandler DataBaseHandler
}
func (ai AppInfo) Get() AppInfo {
@ -299,6 +374,11 @@ func (ai *AppInfo) Init(api WindStandardProtocolAPI) error {
return nil
}
func (ai *AppInfo) InitWSD(api WindStandardDataBaseAPI) error {
WSD = api
return nil
}
func (ai *AppInfo) AddCmd(name string, cmd Cmd) {
ai.CmdMap[name] = cmd
}
@ -311,108 +391,6 @@ 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)
}
}
// VarGet 获取变量
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)
if !ok {
return "", false
}
resStr, ok := res.(string)
if !ok {
return "", false
}
return resStr, true
}
return "", false
}
// GetIntConfig 获取整数配置
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)
if !ok {
return 0, false
}
resInt, ok := res.(int64)
if !ok {
return 0, false
}
return resInt, true
}
return 0, false
}
// GetStringConfig 获取字符串配置
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)
if !ok {
return "", false
}
resStr, ok := res.(string)
if !ok {
return "", false
}
return resStr, true
}
return "", false
}
// GetFloatConfig 获取浮点数配置
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)
if !ok {
return 0, false
}
resFloat, ok := res.(float64)
if !ok {
return 0, false
}
return resFloat, true
}
return 0, false
}
// GetIntSliceConfig 获取整数切片配置
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)
if !ok {
return nil, false
}
resSlice, ok := res.([]int64)
if !ok {
return nil, false
}
return resSlice, true
}
return nil, false
}
// GetStringSliceConfig 获取字符串切片配置
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)
if !ok {
return nil, false
}
resSlice, ok := res.([]string)
if !ok {
return nil, false
}
return resSlice, true
}
return nil, false
}
type AppInfoOption func(ei *AppInfo)
func WithName(name string) AppInfoOption {
@ -483,15 +461,16 @@ func NewApp(opts ...AppInfoOption) AppInfo {
return Ext
}
func NewCmd(name string, description string, solve func(args []string, msg MessageEventInfo)) Cmd {
func (ai *AppInfo) NewCmd(name string, description string, solve func(args []string, msg MessageEventInfo)) Cmd {
return Cmd{
NAME: name,
DESC: description,
SOLVE: solve,
Name: name,
Desc: description,
Solve: solve,
Rule: ai.Rule,
}
}
func NewScheduledTask(name string, description string, cron string, task func()) ScheduledTaskInfo {
func (ai *AppInfo) NewScheduledTask(name string, description string, cron string, task func()) ScheduledTaskInfo {
return ScheduledTaskInfo{
Name: name,
Desc: description,
@ -501,9 +480,10 @@ func NewScheduledTask(name string, description string, cron string, task func())
}
type Cmd struct {
NAME string
DESC string
SOLVE func(args []string, msg MessageEventInfo)
Name string
Desc string
Solve func(args []string, msg MessageEventInfo)
Rule string
}
type MessageEventInfo struct {
@ -745,3 +725,4 @@ type ScheduledTaskInfo struct {
}
var WSP WindStandardProtocolAPI
var WSD WindStandardDataBaseAPI