添加了数据库底层逻辑

This commit is contained in:
Sheyiyuan 2024-12-27 15:56:59 +08:00
parent 11bd212a1d
commit d0b8ebde66
20 changed files with 804 additions and 101 deletions

View File

@ -1,5 +1,11 @@
Copyright (C) 2024 Sheyiyuan Copyright (C) 2024 Sheyiyuan
This project uses the following external libraries:
- goja (https://github.com/dop251/goja), licensed under the MIT License.
- gocron (https://github.com/go - co - op/gocron/v2), also licensed under the MIT License.
Copies of the original licenses can be found in the 'licenses' directory (if applicable).
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

View File

@ -7,7 +7,10 @@ import (
) )
func DEBUG(text string, msg ...interface{}) { func DEBUG(text string, msg ...interface{}) {
pc, file, line, ok := runtime.Caller(2) pc, file, line, ok := runtime.Caller(3)
if !ok {
pc, file, line, ok = runtime.Caller(2)
}
if ok { if ok {
funcName := runtime.FuncForPC(pc).Name() funcName := runtime.FuncForPC(pc).Name()
log.Printf("[DEBUG] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...)) log.Printf("[DEBUG] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...))

View File

@ -515,13 +515,13 @@ func (a *apiInfo) GetGroupMemberList(groupId int64) (Response wba.APIResponseInf
} }
// GetGroupHonorInfo 获取群荣誉信息 // GetGroupHonorInfo 获取群荣誉信息
func (a *apiInfo) GetGroupHonorInfo(groupId int64, userId int64) (Response wba.APIResponseInfo) { func (a *apiInfo) GetGroupHonorInfo(groupId int64, Type string) (Response wba.APIResponseInfo) {
LOG.INFO("获取群荣誉信息(GetGroupHonorInfo)") LOG.INFO("获取群荣誉信息(GetGroupHonorInfo)")
var messageData wba.APIRequestInfo var messageData wba.APIRequestInfo
var err error var err error
messageData.Action = "get_group_honor_info" messageData.Action = "get_group_honor_info"
messageData.Params.GroupId = groupId messageData.Params.GroupId = groupId
messageData.Params.UserId = userId messageData.Params.Type = Type
messageData.Echo, err = GenerateUUID() messageData.Echo, err = GenerateUUID()
if err != nil { if err != nil {
LOG.ERROR("获取群荣誉信息(GetGroupHonorInfo)时生成UUID失败: %v", err) LOG.ERROR("获取群荣誉信息(GetGroupHonorInfo)时生成UUID失败: %v", err)
@ -728,6 +728,14 @@ func (a *apiInfo) Log(content string, args ...interface{}) {
//database模块 //database模块
//TODO: 数据库模块待实现 //TODO: 数据库模块待实现
// 文件管理模块
//TODO: 文件管理模块待实现
//终端连接模块
//TODO: 终端模块待实现
//核心信息调用模块
var AppApi apiInfo var AppApi apiInfo
func GenerateUUID() (string, error) { func GenerateUUID() (string, error) {

View File

@ -16,7 +16,7 @@ func ReloadApps() (total int, success int) {
total = 0 total = 0
success = 0 success = 0
if err != nil { if err != nil {
LOG.ERROR("Error reading apps directory:%v", err) LOG.ERROR("加载应用所在目录失败:%v", err)
return return
} }
@ -39,29 +39,29 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i
pluginPath := filepath.Join(appsDir, file.Name()) pluginPath := filepath.Join(appsDir, file.Name())
p, err := plugin.Open(pluginPath) p, err := plugin.Open(pluginPath)
if err != nil { if err != nil {
LOG.ERROR("Error opening app %s: %v", pluginPath, err) LOG.ERROR("打开应用 %s 时发生错误: %v", pluginPath, err)
return 1, 0 return 1, 0
} }
initSymbol, err := p.Lookup("Application") initSymbol, err := p.Lookup("Application")
if err != nil { if err != nil {
LOG.ERROR("Error finding interface Application in app %s: %v", pluginPath, err) LOG.ERROR("找不到应用 %s 提供的 Application 接口: %v", pluginPath, err)
return 1, 0 return 1, 0
} }
app, ok := initSymbol.(wba.APP) app, ok := initSymbol.(wba.APP)
if !ok { if !ok {
LOG.ERROR("init symbol in app %s is not a right type", pluginPath) LOG.ERROR("应用 %s 提供的 Application 接口不是 wba.APP 类型", pluginPath)
return 1, 0 return 1, 0
} }
err = app.Init(&AppApi) err = app.Init(&AppApi)
if err != nil { if err != nil {
LOG.ERROR("Error initializing app %s: %v", pluginPath, err) LOG.ERROR("初始化应用 %s 失败: %v", pluginPath, err)
} }
CmdMap = mergeMaps(CmdMap, app.Get().CmdMap) CmdMap = mergeMaps(CmdMap, app.Get().CmdMap)
LOG.INFO("App %s initialized successfully", pluginPath) LOG.INFO("应用 %s 加载成功", pluginPath)
return 1, 1 return 1, 1
} }

18
core/cron.go Normal file
View File

@ -0,0 +1,18 @@
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"github.com/robfig/cron/v3"
)
func RegisterCron(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)
}
c.Start()
LOG.INFO("定时任务 %s 注册成功", task.Name)
}

View File

@ -12,7 +12,7 @@ func HandleMessage(msgJson []byte) {
var msg wba.MessageEventInfo var msg wba.MessageEventInfo
err := json.Unmarshal(msgJson, &msg) err := json.Unmarshal(msgJson, &msg)
if err != nil { if err != nil {
LOG.ERROR("Unmarshalling message: %v", err) LOG.ERROR("消息事件反序列化失败: %v", err)
} }
// 处理消息 // 处理消息
LOG.INFO("收到消息:(来自:%v-%v:%v-%v)%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, msg.RawMessage) LOG.INFO("收到消息:(来自:%v-%v:%v-%v)%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, msg.RawMessage)
@ -30,7 +30,7 @@ func HandleNotice(msgJson []byte) {
var notice wba.NoticeEventInfo var notice wba.NoticeEventInfo
err := json.Unmarshal(msgJson, &notice) err := json.Unmarshal(msgJson, &notice)
if err != nil { if err != nil {
LOG.ERROR("Unmarshalling notice: %v", err) LOG.ERROR("通知事件反序列化失败: %v", err)
} }
// TODO: 处理通知 // TODO: 处理通知
} }
@ -39,7 +39,7 @@ func HandleRequest(msgJson []byte) {
var request wba.NoticeEventInfo var request wba.NoticeEventInfo
err := json.Unmarshal(msgJson, &request) err := json.Unmarshal(msgJson, &request)
if err != nil { if err != nil {
LOG.ERROR("Unmarshalling request: %v", err) LOG.ERROR("请求事件反序列化失败: %v", err)
} }
// TODO: 处理请求 // TODO: 处理请求
} }
@ -48,7 +48,7 @@ func HandleMetaEvent(msgJson []byte) {
var meta wba.NoticeEventInfo var meta wba.NoticeEventInfo
err := json.Unmarshal(msgJson, &meta) err := json.Unmarshal(msgJson, &meta)
if err != nil { if err != nil {
LOG.ERROR("Unmarshalling meta: %v", err) LOG.ERROR("元事件反序列化失败: %v", err)
} }
// TODO: 处理元事件 // TODO: 处理元事件
} }

26
core/os.go Normal file
View File

@ -0,0 +1,26 @@
package core
import "runtime"
func GetOS() OS {
return OS{
Arch: runtime.GOARCH,
System: OsNameMap[runtime.GOOS],
}
}
var OsNameMap = map[string]string{
"darwin": "macos",
"linux": "linux",
"windows": "windows",
}
type OS struct {
Version string
Arch string
System string
}
func (o *OS) String() string {
return o.System + "-" + o.Arch
}

View File

@ -155,7 +155,6 @@ func wsAPI(body wba.APIRequestInfo) (Response wba.APIResponseInfo, err error) {
return wba.APIResponseInfo{}, fmt.Errorf("请求发送失败: %v", err) return wba.APIResponseInfo{}, fmt.Errorf("请求发送失败: %v", err)
} }
if body.Action == "get_group_list" || body.Action == "get_member_list" { if body.Action == "get_group_list" || body.Action == "get_member_list" {
// 处理get_group_list和get_member_list请求,直接返回
for { for {
_, message, err := conn.ReadMessage() _, message, err := conn.ReadMessage()
if err != nil { if err != nil {

283
database/database.go Normal file
View File

@ -0,0 +1,283 @@
package database
import (
"ProjectWIND/LOG"
"encoding/json"
"errors"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
)
type unit struct {
Id string
Data map[string]string
}
type User unit
type Group unit
type Global unit
type Database struct {
Id string
Users map[string]User
Groups map[string]Group
Global map[string]Global
//...
// Others map[string]map[string]unit
}
func newDatabase(id string) Database {
// 创建数据库
db := &Database{
Id: id,
Users: make(map[string]User),
Groups: make(map[string]Group),
}
return *db
}
func folderCheck(filename string) {
if _, err := os.Stat(filename); os.IsNotExist(err) {
err := os.MkdirAll(filename, 0755)
if err != nil {
LOG.FATAL("[ERROR]Error occurred while create folder: %v", err)
}
}
}
func fileCheck(filename string) {
// 检查并创建文件
dir := filepath.Dir(filename)
folderCheck(dir)
if _, err := os.Stat(filename); os.IsNotExist(err) {
file, err := os.Create(filename)
if err != nil {
LOG.FATAL("[ERROR]Error occurred while create file: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("[ERROR]Error occurred while close file: %v", err)
}
}(file)
}
}
func writeContent(f *os.File, str string) error {
// 写入内容到文件
if f == nil {
// log.Printf("[ERROR]file is nil")
LOG.ERROR("[ERROR]file is nil")
return errors.New("file is nil")
}
_, err := f.Write([]byte(str))
if err != nil {
LOG.ERROR("[ERROR]Error while write content to file: %v", err)
return err
}
return nil
}
func printContent(file string) (string, error) {
// 读取文件内容
bytes, err := os.ReadFile(file)
if err == nil {
return string(bytes), nil
} else {
return "", err
}
}
func SaveData(db *Database) error {
// 保存数据到文件
dataJson, err := json.Marshal(db)
if err != nil {
LOG.ERROR("[ERROR]:Error while marshal data: %v", err)
return err
}
filename := "./data/database/" + db.Id + ".txt"
file, err := os.Create(filename)
if err != nil {
LOG.ERROR("[ERROR]:Error while create file %s: %v", filename, err)
return err
}
err = writeContent(file, string(dataJson))
if err != nil {
return err
}
return nil
}
func loadData(db *Database) error {
// 读取配置文件
filename := "./data/database/" + db.Id + ".txt"
fileCheck(filename)
dataJson, err := printContent(filename)
if err != nil {
// log.Printf("[ERROR]:Error while read file %s: %v", filename, err)
LOG.ERROR("[ERROR]:Error while read file %s: %v", filename, err)
return err
}
err = json.Unmarshal([]byte(dataJson), db)
if err != nil {
// log.Printf("[ERROR]:Error while unmarshal data: %v", err)
LOG.WARN("[WARNING]:Error while unmarshal data: %v", err)
return err
}
return nil
}
func DataGet(db *Database, category string, id string, key string) (string, bool) {
// 查询数据
switch category {
case "user":
user, ok := db.Users[id]
if !ok {
LOG.WARN("[WARNING]:User %s not found", id)
return "", false
}
if user.Data == nil {
LOG.WARN("[WARNING]:User %s's data is nil", id)
return "", false
}
value, ok := user.Data[key]
if !ok {
LOG.WARN("[WARNING]:User %s's data %s not found", id, key)
return "", false
}
return value, true
case "group":
group, ok := db.Groups[id]
if !ok {
LOG.WARN("[WARNING]:Group %s not found", id)
return "", false
}
if group.Data == nil {
LOG.WARN("[WARNING]:Group %s's data is nil", id)
return "", false
}
value, ok := group.Data[key]
if !ok {
LOG.WARN("[WARNING]:Group %s's data %s not found", id, key)
return "", false
}
return value, true
case "global":
global, ok := db.Global[id]
if !ok {
LOG.WARN("[WARNING]:Global %s not found", id)
return "", false
}
if global.Data == nil {
LOG.WARN("[WARNING]:Global %s's data is nil", id)
return "", false
}
value, ok := global.Data[key]
if !ok {
LOG.WARN("[WARNING]:Global %s's data %s not found", id, key)
return "", false
}
return value, true
default:
LOG.ERROR("[ERROR]:Invalid category %s", category)
return "", false
}
}
func DataSet(db *Database, category string, id string, key string, value string) {
// 修改数据
switch category {
case "user":
user, ok := db.Users[id]
if !ok {
db.Users[id] = User{
Id: id,
Data: make(map[string]string),
}
user = db.Users[id]
}
if user.Data == nil {
user.Data = make(map[string]string)
}
user.Data[key] = value
case "group":
group, ok := db.Groups[id]
if !ok {
db.Groups[id] = Group{
Id: id,
Data: make(map[string]string),
}
group = db.Groups[id]
}
if group.Data == nil {
group.Data = make(map[string]string)
}
group.Data[key] = value
case "global":
global, ok := db.Global[id]
if !ok {
db.Global[id] = Global{
Id: id,
Data: make(map[string]string),
}
global = db.Global[id]
}
if global.Data == nil {
global.Data = make(map[string]string)
}
global.Data[key] = value
default:
LOG.ERROR("[ERROR]:Invalid category %s", category)
}
}
func keepDatabase(db *Database) {
// 创建一个通道用于接收信号
dataChan := make(chan os.Signal, 1)
// 监听指定的信号如SIGINT (Ctrl+C) 和 SIGTERM
signal.Notify(dataChan, syscall.SIGINT, syscall.SIGTERM)
// 定义一个Ticker用于每1小时触发一次保存操作
saveTicker := time.NewTicker(3600 * time.Second)
defer saveTicker.Stop()
// 启动一个goroutine等待信号和定时保存
go func() {
for {
select {
case <-dataChan:
// 接收到信号,保存数据并退出程序
LOG.INFO("Received signal, saving data and exiting...")
err := SaveData(db)
if err != nil {
return
}
os.Exit(0)
case <-saveTicker.C:
// 定时保存数据
LOG.INFO("Saving data automatically...")
err := SaveData(db)
if err != nil {
return
}
}
}
}()
select {} // 阻塞主goroutine
}
func Start() *Database {
// 启动并检查程序
LOG.INFO("Starting database ...")
db := newDatabase("datamap")
err := loadData(&db)
if err != nil {
return nil
}
LOG.INFO("Database started successfully.")
keepDatabase(&db)
return &db
}

View File

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

11
doc.go
View File

@ -9,7 +9,7 @@ const (
-h, --help 显示帮助文档 -h, --help 显示帮助文档
-v, --version 显示版本信息 -v, --version 显示版本信息
-r, --run 运行wind -r, --run 运行wind
-s, 创建Linux service -s, 创建Linux service(仅Linux)
-i, --init 初始化配置文件 -i, --init 初始化配置文件
Website: Website:
github.com/Sheyiyuan/ProjectWIND github.com/Sheyiyuan/ProjectWIND
@ -18,4 +18,13 @@ const (
-w, -w,
` `
version string = `WIND v0.1.0` version string = `WIND v0.1.0`
logo string = `
_ __ ____ _ __ ____
| | / / / _/ / | / / / __ \
| | /| / / / / / |/ / / / / /
| |/ |/ / _/ / / /| / / /_/ /
|__/|__/ /___/ /_/ |_/ /_____/
`
) )

View File

@ -8,23 +8,28 @@
</head> </head>
<body> <body>
<header> <header>
<h1>Wind</h1> <div class="container">
<p>Wind is not dice</p> <div>
<p></p> <h1>Wind</h1>
<a href="#" class="button">查看手册</a> <p>Wind is not dice</p>
<a href="#" class="button">GitHub</a> <p>一个简单易用的bot框架</p>
<a href="./developer_doc.html" class="button">查看手册</a>
<a href="#" class="button">GitHub</a>
</div>
<img class="logo" src="../icon.png" alt="logo" width="256">
</div>
</header> </header>
<div class="container"> <div class="container">
<div class="card"> <div class="card">
<h2>开箱即用</h2> <h2>开箱即用📦</h2>
<p>多种部署方式,快速部署于 Windows/Linux/macOS 等主流架构平台。</p> <p>多种部署方式,快速部署于 Windows/Linux/macOS 等主流架构平台。</p>
</div> </div>
<div class="card"> <div class="card">
<h2>内存轻量</h2> <h2>内存轻量🚀</h2>
<p>不依赖框架加载,不依赖 Electron内存占用低至 50-100 MB。</p> <p>不依赖框架加载,不依赖 Electron内存占用低至 50-100 MB。</p>
</div> </div>
<div class="card"> <div class="card">
<h2>适配快速</h2> <h2>适配快速💻</h2>
<p>使用web ui可视化配置远程管理一键部署。</p> <p>使用web ui可视化配置远程管理一键部署。</p>
</div> </div>
</div> </div>

View File

@ -32,8 +32,8 @@ p {
width: 250px; width: 250px;
} }
.button { .button {
background-color: #ee82ee; background-color: #0fc;
color: white; color: #555;
padding: 10px 20px; padding: 10px 20px;
border: none; border: none;
border-radius: 5px; border-radius: 5px;

2
go.mod
View File

@ -3,3 +3,5 @@ module ProjectWIND
go 1.23.2 go 1.23.2
require github.com/gorilla/websocket v1.5.3 require github.com/gorilla/websocket v1.5.3
require github.com/robfig/cron/v3 v3.0.1 // indirect

2
go.sum
View File

@ -1,2 +1,4 @@
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=

View File

@ -1,10 +1,13 @@
package main package main
import ( import (
"ProjectWIND/core"
"fmt" "fmt"
"os" "os"
) )
var CoreOs = core.GetOS()
func main() { func main() {
//如果没有参数则启动WebUI //如果没有参数则启动WebUI
if len(os.Args) <= 1 { if len(os.Args) <= 1 {
@ -14,6 +17,7 @@ func main() {
} }
cmdArgs := os.Args[1:] cmdArgs := os.Args[1:]
if cmdArgs[0] == "-h" || cmdArgs[0] == "--help" { if cmdArgs[0] == "-h" || cmdArgs[0] == "--help" {
fmt.Printf(logo)
fmt.Printf("%v\n", helpDoc) fmt.Printf("%v\n", helpDoc)
return return
} }
@ -29,7 +33,8 @@ func main() {
} }
if cmdArgs[0] == "-v" || cmdArgs[0] == "--version" { if cmdArgs[0] == "-v" || cmdArgs[0] == "--version" {
// 显示版本信息 // 显示版本信息
fmt.Printf(`%v\n`, version) fmt.Printf(logo)
fmt.Printf("%v \n架构%v\n", version, CoreOs.String())
return return
} }
if cmdArgs[0] == "-s" || cmdArgs[0] == "--service" { if cmdArgs[0] == "-s" || cmdArgs[0] == "--service" {
@ -43,6 +48,6 @@ func main() {
startProtocol() startProtocol()
return return
} }
fmt.Println("Invalid command.") fmt.Println("未知命令,请使用-h查看帮助。")
return return
} }

View File

@ -23,4 +23,21 @@ TODO:
WIND全称WIND is not dice是一个基于 Go 语言开发bot框架旨在以高自由度完成各种功能。 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 代码来实现一些特定的业务规则处理等]。它极大地丰富了项目的功能和灵活性,让我们能够更高效地开发出具有特色的功能模块。
## 2. gocron
- **库名称**gocron
- **仓库地址**[https://github.com/go - co - op/gocron/v2](https://github.com/go - co -· op/gocron/v2)
- **用途说明**gocron 是一个出色的任务调度库。在本项目里,它被用于[详细的任务调度应用场景,比如定期执行数据同步任务、定时清理临时文件或缓存数据等],确保了项目中的各种定时任务能够精准、可靠地执行。其简洁易用的 API 设计大大降低了我们实现复杂任务调度逻辑的难度,为项目的稳定运行提供了有力保障。
非常感谢 `goja``gocron` 项目团队的开源贡献,使得我们的项目开发能够借助这些优秀的工具快速推进,为用户带来更好的体验。
---
后面没有了,开发者很懒,什么都没写。 后面没有了,开发者很懒,什么都没写。

View File

@ -22,7 +22,7 @@ func initCore() string {
err := checkAndUpdateConfig("./data/core.json") err := checkAndUpdateConfig("./data/core.json")
if err != nil { if err != nil {
LOG.FATAL("Failed to initialize core.json file: %v", err) LOG.FATAL("初始化时,加载配置文件 ./data/core.json 失败: %v", err)
} }
// 创建日志文件 // 创建日志文件
logFile := fmt.Sprintf("./data/log/WIND_CORE_%s.log", time.Now().Format("20060102150405")) logFile := fmt.Sprintf("./data/log/WIND_CORE_%s.log", time.Now().Format("20060102150405"))
@ -30,24 +30,24 @@ func initCore() string {
if os.IsNotExist(err) { if os.IsNotExist(err) {
file, err := os.Create(logFile) file, err := os.Create(logFile)
if err != nil { if err != nil {
LOG.FATAL("Failed to create log file: %v", err) LOG.FATAL("初始化时,创建日志文件失败: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close log file: %v", err) LOG.FATAL("无法关闭日志文件: %v", err)
} }
}(file) }(file)
} }
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil { if err != nil {
LOG.FATAL("Failed to open log file: %v", err) LOG.FATAL("初始化时,无法打开日志文件: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close log file: %v", err) LOG.FATAL("无法关闭日志文件: %v", err)
} }
}(file) }(file)
@ -63,7 +63,7 @@ func checkAndUpdateConfig(configPath string) error {
// 如果不存在,则创建该文件夹 // 如果不存在,则创建该文件夹
err := os.Mkdir("./data/", 0755) err := os.Mkdir("./data/", 0755)
if err != nil { if err != nil {
LOG.FATAL("Failed to create data folder: %v", err) LOG.FATAL("初始化时创建data文件夹失败: %v", err)
} }
} }
@ -72,12 +72,12 @@ func checkAndUpdateConfig(configPath string) error {
// 如果不存在,则创建该文件 // 如果不存在,则创建该文件
file, err := os.Create("./data/core.json") file, err := os.Create("./data/core.json")
if err != nil { if err != nil {
LOG.FATAL("Failed to create core.json file: %v", err) LOG.FATAL("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close core.json file: %v", err) LOG.FATAL("关闭 ./data/core.json 配置文件失败: %v", err)
} }
}(file) }(file)
} }
@ -99,7 +99,7 @@ func checkAndUpdateConfig(configPath string) error {
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close core.json file: %v", err) LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
} }
}(file) }(file)
@ -140,19 +140,19 @@ func checkAndUpdateConfig(configPath string) error {
// 将格式化后的JSON字符串写入文件 // 将格式化后的JSON字符串写入文件
file, err = os.Create("./data/core.json") file, err = os.Create("./data/core.json")
if err != nil { if err != nil {
LOG.FATAL("Error creating core.json file:%v", err) LOG.FATAL("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
return err return err
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close core.json file: %v", err) LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
} }
}(file) }(file)
_, err = file.Write(formattedJSON) _, err = file.Write(formattedJSON)
if err != nil { if err != nil {
LOG.FATAL("Error writing to core.json file: %v", err) LOG.FATAL("初始化时,写入 ./data/core.json 配置文件失败: %v", err)
return err return err
} }
@ -169,24 +169,43 @@ func checkAndUpdateConfig(configPath string) error {
err = checkDataFolderExistence("./data/app/") err = checkDataFolderExistence("./data/app/")
if err != nil { if err != nil {
LOG.FATAL("Failed to create app folder: %v", err) LOG.FATAL("创建应用文件夹 ./data/app/ 失败: %v", err)
return err return err
} }
err = checkDataFolderExistence("./data/images/") err = checkDataFolderExistence("./data/images/")
if err != nil { if err != nil {
LOG.FATAL("Failed to create images folder: %v", err) LOG.FATAL("创建图片文件夹 ./data/images/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/files/")
if err != nil {
LOG.FATAL("创建文件文件夹 ./data/files/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/videos/")
if err != nil {
LOG.FATAL("创建视频文件夹 ./data/videos/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/audios/")
if err != nil {
LOG.FATAL("创建音频文件夹 ./data/audios/ 失败: %v", err)
return err return err
} }
err = checkDataFolderExistence("./data/database/") err = checkDataFolderExistence("./data/database/")
if err != nil { if err != nil {
LOG.FATAL("Failed to create database folder: %v", err) LOG.FATAL("创建数据库文件夹 ./data/database/ 失败: %v", err)
return err return err
} }
err = checkDataFolderExistence("./data/log/") err = checkDataFolderExistence("./data/log/")
if err != nil { if err != nil {
LOG.FATAL("Failed to create log folder: %v", err) LOG.FATAL("创建日志文件夹 ./data/log/ 失败: %v", err)
return err return err
} }
err = checkDataFolderExistence("./data/app/configs/")
if err != nil {
LOG.FATAL("创建应用配置文件夹 ./data/app/configs/ 失败: %v", err)
}
return nil return nil
} }
@ -201,12 +220,12 @@ func startWebUI() {
// 打开日志文件 // 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil { if err != nil {
LOG.FATAL("Failed to open log file: %v", err) LOG.FATAL("打开日志文件失败: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close log file: %v", err) LOG.FATAL("无法关闭日志文件: %v", err)
} }
}(file) }(file)
// 设置日志输出到文件 // 设置日志输出到文件
@ -228,12 +247,12 @@ func registerService() {
// 打开日志文件 // 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil { if err != nil {
LOG.FATAL("Failed to create log file: %v", err) LOG.FATAL("无法打开日志文件: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close log file: %v", err) LOG.FATAL("无法关闭日志文件: %v", err)
} }
}(file) }(file)
// 设置日志输出到文件 // 设置日志输出到文件
@ -250,12 +269,12 @@ func startProtocol() {
// 打开日志文件 // 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil { if err != nil {
LOG.FATAL("Failed to create log file: %v", err) LOG.FATAL("无法打开日志文件: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close log file: %v", err) LOG.FATAL("无法关闭日志文件: %v", err)
} }
}(file) }(file)
// 设置日志输出到文件 // 设置日志输出到文件
@ -266,19 +285,19 @@ func startProtocol() {
var config typed.CoreConfigInfo var config typed.CoreConfigInfo
file, err = os.Open("./data/core.json") file, err = os.Open("./data/core.json")
if err != nil { if err != nil {
LOG.FATAL("Failed to open core.json file: %v", err) LOG.FATAL("无法打开配置文件 ./data/core.json: %v", err)
} }
defer func(file *os.File) { defer func(file *os.File) {
err := file.Close() err := file.Close()
if err != nil { if err != nil {
LOG.FATAL("Failed to close core.json file: %v", err) LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
} }
}(file) }(file)
decoder := json.NewDecoder(file) decoder := json.NewDecoder(file)
err = decoder.Decode(&config) err = decoder.Decode(&config)
if err != nil { if err != nil {
LOG.FATAL("Failed to decode config file when linking to protocol: %v", err) LOG.FATAL("连接协议时,解析配置文件 ./data/core.json 失败: %v", err)
} }
//获取协议地址 //获取协议地址
protocolAddr := config.ProtocolAddr protocolAddr := config.ProtocolAddr
@ -290,7 +309,7 @@ func startProtocol() {
err = core.WebSocketHandler(protocolAddr, token) err = core.WebSocketHandler(protocolAddr, token)
if err != nil { if err != nil {
// 如果发生错误,记录错误并退出程序 // 如果发生错误,记录错误并退出程序
LOG.FATAL("Failed to start WebSocket link program: %v", err) LOG.FATAL("连接协议时,启动 WebSocket 处理程序失败: %v", err)
} }
return return
} }

227
wba/readme.md Normal file
View File

@ -0,0 +1,227 @@
# WBA 文档
WBA是用于对接WIND核心和APP通信的接口规定了应用结构规范并提供了与wind核心交互的接口定义。
## 目录
- [1. 应用结构规范](#1-应用结构规范)
- [1.1 应用结构](#11-应用结构)
- [1.2 Application结构体的成员](#12-application结构体的成员)
- [1.3 Application的注册](#13-application的注册)
- [2. 接口定义](#2-接口定义)
- [2.1 protocol模块](#21-protocol模块)
- [2.2 Log模块](#22-log模块)
- [2.3 数据库模块](#23-数据库模块)
- [2.4 文件管理模块](#24-文件管理模块)
- [2.5 远程控制模块](#25-远程控制模块)
## 1. 应用结构规范
### 1.1 应用结构
wba会向wind核心提供两个用于调用应用逻辑的方法
- init(Wind):
初始化wba传入wind实例应用可以通过传入的wind实例调用wind提供的接口具体参见[wind接口定义](#2-接口定义)。
- Get()
核心会通过该方法获取应用中定义的对象,该方法返回[Application 结构体](#12-application结构体的成员)
### 1.2 Application结构体的成员
| 字段名 | 类型 | 默认值 | 说明 |
|---------------------|------------------------------|:-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name | string | - | 应用名称,会被用于帮助文档的检索 |
| Version | string | - | 应用版本号 |
| Author | string | - | 作者 |
| Description | string | - | 应用详细描述,会被写入到帮助文档中 |
| Namespace | string | `"PUBLIC"` | 命名空间,用于区分不同应用 |
| Homepage | string | - | 应用主页地址 |
| License | string | `"MIT"` | 应用许可证 |
| AppType | string | `"fun"` | 应用类型,可选值有以下不同级别:<ul><li>`fun`:娱乐级应用</li><li>`assist`:规则辅助级应用</li><li>`rule`:规则级应用</li><li>`system`:系统级应用</ul> <br/>应用调用时的优先级从低到高依次为`fun``assist``rule``system`。同等级的应用,如有关联的高级别应用,则优先级由高级别的应用决定。否则会按照注册顺序决定优先级。 |
| Rule | string | - | 仅在AppType为`assist``rule`时有效,用于确认规则类型 |
| CmdMap | map[string] Cmd | - | 存储命令名称和命令实例的映射map |
| MessageEventHandler | func(msg MessageEventInfo) | - | 在收到消息事件时触发的事件处理函数 |
| NoticeEventHandler | func(msg NoticeEventInfo) | - | 在收到通知事件时触发的事件处理函数 |
| RequestEventHandler | func(msg RequestEventInfo) | - | 在收到请求事件时触发的事件处理函数 |
| MetaEventHandler | func(msg MetaEventInfo) | - | 在收到元事件时触发的事件处理函数 |
| ScheduledTasks | map[string]ScheduledTaskInfo | - | 定时任务列表key为任务名称value为任务信息结构体 |
| API | map[string]interface{} | - | 应用对外提供的API列表可被其他应用调用其中key为API名称value为API实例 |
#### Cmd结构体的成员
| 字段名 | 类型 | 说明 |
|-------|--------|------------------------------------------------|
| NAME | string | 命令的名称 |
| DESC | string | 命令的描述,会被用于帮助信息的展示 |
| SOLVE | func | 解决命令的函数,接收参数为命令参数的字符串切片和 `MessageEventInfo` 类型 |
#### ScheduledTaskInfo结构体的成员
| 字段名 | 类型 | 说明 |
|------|--------|----------------|
| Name | string | 任务名称 |
| Desc | string | 任务描述 |
| Task | func() | 任务执行的函数 |
| Cron | string | 定时任务的 Cron 表达式 |
### 1.3 Application的注册
下面给出一个注册应用的示例代码:
#### 注册应用
```go
package main
import (
"github.com/wind/wba"
)
func appInit() wba.AppInfo {
// 写入应用信息
app := wba.NewApp(
wba.WithName("app_demo"), // 应用名称
wba.WithAuthor("WIND"), // 作者
wba.WithVersion("1.0.0"), // 版本
wba.WithDescription("This is a demo application"), // 应用描述
wba.WithNamespace("app_demo"), // 命名空间, 私有数据库请使用应用的名称, 公共数据库请使用"PUBLIC"
wba.WithWebUrl("https://github.com/wind/app_demo"), // 应用主页
wba.WithLicense("MIT"), // 应用许可证
)
return app
}
// Application 向核心暴露的应用接口,标识符为Application, 不可修改
var Application = appInit()
```
定义初始化函数 `appInit()` ,返回 `wba.AppInfo` 类型,该类型包含应用的基本信息,包括名称、版本、作者、描述、命名空间、主页、许可证等。并使用固定标识符进行接口暴露,该标识符为 `Application`,不可修改。
在初始化时,使用 `wba.NewApp()` 方法创建 `wba.AppInfo` 实例,并使用 `wba.WithName()``wba.WithAuthor()``wba.WithVersion()``wba.WithDescription()``wba.WithNamespace()``wba.WithWebUrl()``wba.WithLicense()``wba.WithAppType()``wba.WithRule()` 方法设置应用的基本信息。未设置的字段将使用默认值。
对于其他的接口如命令、定时任务等wba提供了一些函数和方法用于设置这些接口。
#### 注册命令
```go
// 定义命令
cmdTest := wba.NewCmd(
//命令名称
"app",
//命令介绍
"插件测试",
func(args []string, msg wba.MessageEventInfo) {
val := args[0]
log.Println("app_demo cmdTest", val)
switch val {
case "help":
{
wba.Wind.SendMsg(msg, "app_demo help", false)
}
default:
{
wba.Wind.SendMsg(msg, "Hello, wind app!", false)
return
}
}
},
)
// 将命令添加到应用命令列表中
app.AddCmd("app", cmdTest)
//可以为同一个命令注册多个名称
app.AddCmd("test", cmdTest)
```
使用 `wba.NewCmd()` 方法创建 `wba.Cmd` 实例,并设置命令的名称、描述、解决函数。之后使用 `app.AddCmd()` 方法将命令添加到应用命令列表中。
#### 注册定时任务
```go
// 定义定时任务
taskTest := wba.ScheduledTaskinfo{
Name: "task_test",
Desc: "定时任务测试",
Task: func() {
log.Println("task_test")
},
Cron: "*/1 * * * *",
}
// 将定时任务添加到应用定时任务列表中
app.AddScheduledTask(taskTest)
}
```
使用 `wba.ScheduledTaskinfo` 结构体定义定时任务,并设置任务名称、描述、执行函数、定时任务的 Cron 表达式。之后使用 `app.AddScheduledTask()` 方法将定时任务添加到应用定时任务列表中。
## 2. 接口定义
wind在初始化时会调用 `init(Wind)` 方法,传入 `Wind` 实例应用可以通过该实例调用wind提供的接口。
wind实例提供的接口可以分为下面几个部分
### 2.1 Protocol模块
所有API请求按照OneBot11标准使用JSON格式进行数据交换。api命名为由原文档中蛇形命名法改为双驼峰命名法。
关于API的详细信息请参考[OneBot文档](https://github.com/botuniverse/onebot-11/blob/master/README.md)。
```go
SendMsg(msg MessageEventInfo, message string, autoEscape bool)
SendPrivateMsg(msg MessageEventInfo, message string, autoEscape bool)
SendGroupMsg(msg MessageEventInfo, message string, autoEscape bool)
DeleteMsg(msg MessageEventInfo)
SendLike(userId int64, times int)
SetGroupKick(groupId int64, userId int64, rejectAddRequest bool)
SetGroupBan(groupId int64, userId int64, duration int32)
SetGroupWholeBan(groupId int64, enable bool)
SetGroupAdmin(groupId int64, userId int64, enable bool)
SetGroupLeave(groupId int64, isDismiss bool)
SetGroupCard(groupId int64, userId int64, card string)
SetGroupName(groupId int64, groupName string)
SetGroupSpecialTitle(groupId int64, userId int64, specialTitle string, duration int32)
SetFriendAddRequest(flag string, approve bool, remark string)
SetGroupAddRequest(flag string, subType string, approve bool, reason string)
GetLoginInfo() APIResponseInfo
GetVersionInfo() APIResponseInfo
GetMsg(msgId int32) APIResponseInfo
GetForwardMsg(msgId string) APIResponseInfo
GetGroupList() APIResponseInfo
GetGroupMemberList(groupId int64) APIResponseInfo
GetGroupMemberInfo(groupId int64, userId int64, noCache bool) APIResponseInfo
GetFriendList() APIResponseInfo
GetStrangerInfo(userId int64, noCache bool) APIResponseInfo
GetGroupInfo(groupId int64, noCache bool) APIResponseInfo
GetGroupHonorInfo(groupId int64, Type string) APIResponseInfo
GetStatus() APIResponseInfo
GetCookies(domain string) APIResponseInfo
GetCSRFToken() APIResponseInfo
GetCredentials(domain string) APIResponseInfo
GetImage(file string) APIResponseInfo
GetRecord(file string, outFormat string) APIResponseInfo
CanSendImage() APIResponseInfo
CanSendRecord() APIResponseInfo
SetRestart(delay int32)
CleanCache()
```
### 2.2 Log模块
1. 日志模块使用go-logging库日志级别分为DEBUG、INFO、WARN、ERROR。
2. 日志模块提供LogWith方法可以自定义日志级别调用级别为DEBUG时会打印输出调用者的文件名、函数名、行号。
3. 日志模块提供Log方法默认日志级别为INFO。
### 2.3 数据库模块
### 2.4 文件管理模块
### 2.5 远程控制模块

View File

@ -1,13 +1,11 @@
package wba package wba
import ( import (
"errors"
"fmt" "fmt"
) )
type APP interface { type APP interface {
Get() AppInfo Get() AppInfo
Run(cmd string, args []string, msg MessageEventInfo) error
Init(api WindAPI) error Init(api WindAPI) error
} }
@ -29,113 +27,132 @@ type WindAPI interface {
SetGroupAddRequest(flag string, subType string, approve bool, reason string) SetGroupAddRequest(flag string, subType string, approve bool, reason string)
GetLoginInfo() APIResponseInfo GetLoginInfo() APIResponseInfo
GetVersionInfo() APIResponseInfo GetVersionInfo() APIResponseInfo
GetMsg(msgId int32) APIResponseInfo
GetForwardMsg(msgId string) APIResponseInfo
GetGroupList() APIResponseInfo
GetGroupMemberList(groupId int64) APIResponseInfo
GetGroupMemberInfo(groupId int64, userId int64, noCache bool) APIResponseInfo
GetFriendList() APIResponseInfo
GetStrangerInfo(userId int64, noCache bool) APIResponseInfo
GetGroupInfo(groupId int64, noCache bool) APIResponseInfo
GetGroupHonorInfo(groupId int64, Type string) APIResponseInfo
GetStatus() APIResponseInfo
GetCookies(domain string) APIResponseInfo
GetCSRFToken() APIResponseInfo
GetCredentials(domain string) APIResponseInfo
GetImage(file string) APIResponseInfo
GetRecord(file string, outFormat string) APIResponseInfo
CanSendImage() APIResponseInfo
CanSendRecord() APIResponseInfo
SetRestart(delay int32)
CleanCache()
LogWith(level string, log string, args ...interface{}) LogWith(level string, log string, args ...interface{})
Log(log string, args ...interface{}) Log(log string, args ...interface{})
} }
type AppInfo struct { type AppInfo struct {
name string Name string
version string Version string
author string Author string
description string Description string
namespace string Namespace string
webUrl string Homepage string
license string License string
appType string AppType string
rule string Rule string
CmdMap map[string]Cmd CmdMap map[string]Cmd
MessageEventHandler func(msg MessageEventInfo)
NoticeEventHandler func(msg NoticeEventInfo)
RequestEventHandler func(msg RequestEventInfo)
MetaEventHandler func(msg MetaEventInfo)
ScheduledTasks map[string]ScheduledTaskInfo
API map[string]interface{}
} }
func (ei AppInfo) Get() AppInfo { func (ai AppInfo) Get() AppInfo {
return ei return ai
} }
func (ei *AppInfo) Run(cmd string, args []string, msg MessageEventInfo) error { func (ai *AppInfo) Init(api WindAPI) error {
_, ok := ei.CmdMap[cmd]
if !ok {
return errors.New("cmd not found")
}
ei.CmdMap[cmd].SOLVE(args, msg)
return nil
}
func (ei *AppInfo) Init(api WindAPI) error {
Wind = api Wind = api
return nil return nil
} }
func (ei *AppInfo) AddCmd(name string, cmd Cmd) error { func (ai *AppInfo) AddCmd(name string, cmd Cmd) {
ei.CmdMap[name] = cmd ai.CmdMap[name] = cmd
return nil }
func (ai *AppInfo) AddNoticeEventHandler(ScheduledTask ScheduledTaskInfo) {
ai.ScheduledTasks[ScheduledTask.Name] = ScheduledTask
} }
type AppInfoOption func(ei *AppInfo) type AppInfoOption func(ei *AppInfo)
func WithName(name string) AppInfoOption { func WithName(name string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.name = name ei.Name = name
} }
} }
func WithVersion(version string) AppInfoOption { func WithVersion(version string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.version = version ei.Version = version
} }
} }
func WithAuthor(author string) AppInfoOption { func WithAuthor(author string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.author = author ei.Author = author
} }
} }
func WithDescription(description string) AppInfoOption { func WithDescription(description string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.description = description ei.Description = description
} }
} }
func WithNamespace(namespace string) AppInfoOption { func WithNamespace(namespace string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.namespace = namespace ei.Namespace = namespace
} }
} }
func WithWebUrl(webUrl string) AppInfoOption { func WithWebUrl(webUrl string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.webUrl = webUrl ei.Homepage = webUrl
} }
} }
func WithLicense(license string) AppInfoOption { func WithLicense(license string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.license = license ei.License = license
} }
} }
func WithAppType(appType string) AppInfoOption { func WithAppType(appType string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.appType = appType ei.AppType = appType
} }
} }
func WithRule(rule string) AppInfoOption { func WithRule(rule string) AppInfoOption {
return func(ei *AppInfo) { return func(ei *AppInfo) {
ei.rule = fmt.Sprintf("rule_%s", rule) ei.Rule = fmt.Sprintf("rule_%s", rule)
} }
} }
func NewApp(opts ...AppInfoOption) AppInfo { func NewApp(opts ...AppInfoOption) AppInfo {
Ext := AppInfo{ Ext := AppInfo{
name: "Wind", Name: "Wind",
version: "v1.0.0", Version: "v1.0.0",
author: "Wind", Author: "Wind",
description: "A simple and easy-to-use bot framework", Description: "A simple and easy-to-use bot framework",
namespace: "wind", Namespace: "PUBLIC",
webUrl: "https://github.com/Sheyiyuan/wind_app_model", Homepage: "https://github.com/Sheyiyuan/wind_app_model",
license: "MIT", License: "MIT",
appType: "fun", AppType: "fun",
rule: "none", Rule: "none",
CmdMap: make(map[string]Cmd), CmdMap: make(map[string]Cmd),
} }
for _, opt := range opts { for _, opt := range opts {
@ -152,6 +169,15 @@ func NewCmd(name string, description string, solve func(args []string, msg Messa
} }
} }
func NewScheduledTask(name string, description string, cron string, task func()) ScheduledTaskInfo {
return ScheduledTaskInfo{
Name: name,
Desc: description,
Cron: cron,
Task: task,
}
}
type Cmd struct { type Cmd struct {
NAME string NAME string
DESC string DESC string
@ -214,7 +240,7 @@ type MetaEventInfo struct {
type FileInfo struct { type FileInfo struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"Name,omitempty"`
Size int64 `json:"size,omitempty"` Size int64 `json:"size,omitempty"`
Busid int64 `json:"bucket,omitempty"` Busid int64 `json:"bucket,omitempty"`
} }
@ -233,7 +259,7 @@ type SenderInfo struct {
type AnonymousInfo struct { type AnonymousInfo struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"Name,omitempty"`
Flag string `json:"flag,omitempty"` Flag string `json:"flag,omitempty"`
} }
@ -363,9 +389,10 @@ type HonorInfo struct {
UserId int64 `json:"user_id,omitempty"` UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"` Nickname string `json:"nickname,omitempty"`
Avatar string `json:"avatar,omitempty"` Avatar string `json:"avatar,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"Description,omitempty"`
} }
// SegmentInfo 消息段
type SegmentInfo struct { type SegmentInfo struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Data SegmentDataInfo `json:"data,omitempty"` Data SegmentDataInfo `json:"data,omitempty"`
@ -388,4 +415,11 @@ type SegmentDataInfo struct {
Data string `json:"data,omitempty"` Data string `json:"data,omitempty"`
} }
type ScheduledTaskInfo struct {
Name string `json:"Name,omitempty"`
Desc string `json:"desc,omitempty"`
Task func() `json:"task,omitempty"`
Cron string `json:"cron,omitempty"`
}
var Wind WindAPI var Wind WindAPI