forked from ProjectWIND/ProjectWIND
添加了数据库底层逻辑
This commit is contained in:
parent
11bd212a1d
commit
d0b8ebde66
6
LICENSE
6
LICENSE
@ -1,5 +1,11 @@
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
@ -7,7 +7,10 @@ import (
|
||||
)
|
||||
|
||||
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 {
|
||||
funcName := runtime.FuncForPC(pc).Name()
|
||||
log.Printf("[DEBUG] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...))
|
||||
|
12
core/api.go
12
core/api.go
@ -515,13 +515,13 @@ func (a *apiInfo) GetGroupMemberList(groupId int64) (Response wba.APIResponseInf
|
||||
}
|
||||
|
||||
// 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)")
|
||||
var messageData wba.APIRequestInfo
|
||||
var err error
|
||||
messageData.Action = "get_group_honor_info"
|
||||
messageData.Params.GroupId = groupId
|
||||
messageData.Params.UserId = userId
|
||||
messageData.Params.Type = Type
|
||||
messageData.Echo, err = GenerateUUID()
|
||||
if err != nil {
|
||||
LOG.ERROR("获取群荣誉信息(GetGroupHonorInfo)时,生成UUID失败: %v", err)
|
||||
@ -728,6 +728,14 @@ func (a *apiInfo) Log(content string, args ...interface{}) {
|
||||
//database模块
|
||||
//TODO: 数据库模块待实现
|
||||
|
||||
// 文件管理模块
|
||||
//TODO: 文件管理模块待实现
|
||||
|
||||
//终端连接模块
|
||||
//TODO: 终端模块待实现
|
||||
|
||||
//核心信息调用模块
|
||||
|
||||
var AppApi apiInfo
|
||||
|
||||
func GenerateUUID() (string, error) {
|
||||
|
@ -16,7 +16,7 @@ func ReloadApps() (total int, success int) {
|
||||
total = 0
|
||||
success = 0
|
||||
if err != nil {
|
||||
LOG.ERROR("Error reading apps directory:%v", err)
|
||||
LOG.ERROR("加载应用所在目录失败:%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -39,29 +39,29 @@ func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta i
|
||||
pluginPath := filepath.Join(appsDir, file.Name())
|
||||
p, err := plugin.Open(pluginPath)
|
||||
if err != nil {
|
||||
LOG.ERROR("Error opening app %s: %v", pluginPath, err)
|
||||
LOG.ERROR("打开应用 %s 时发生错误: %v", pluginPath, err)
|
||||
return 1, 0
|
||||
}
|
||||
|
||||
initSymbol, err := p.Lookup("Application")
|
||||
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
|
||||
}
|
||||
|
||||
app, ok := initSymbol.(wba.APP)
|
||||
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
|
||||
}
|
||||
|
||||
err = app.Init(&AppApi)
|
||||
if err != nil {
|
||||
LOG.ERROR("Error initializing app %s: %v", pluginPath, err)
|
||||
LOG.ERROR("初始化应用 %s 失败: %v", pluginPath, err)
|
||||
}
|
||||
|
||||
CmdMap = mergeMaps(CmdMap, app.Get().CmdMap)
|
||||
LOG.INFO("App %s initialized successfully", pluginPath)
|
||||
LOG.INFO("应用 %s 加载成功", pluginPath)
|
||||
return 1, 1
|
||||
|
||||
}
|
||||
|
18
core/cron.go
Normal file
18
core/cron.go
Normal 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)
|
||||
}
|
@ -12,7 +12,7 @@ func HandleMessage(msgJson []byte) {
|
||||
var msg wba.MessageEventInfo
|
||||
err := json.Unmarshal(msgJson, &msg)
|
||||
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)
|
||||
@ -30,7 +30,7 @@ func HandleNotice(msgJson []byte) {
|
||||
var notice wba.NoticeEventInfo
|
||||
err := json.Unmarshal(msgJson, ¬ice)
|
||||
if err != nil {
|
||||
LOG.ERROR("Unmarshalling notice: %v", err)
|
||||
LOG.ERROR("通知事件反序列化失败: %v", err)
|
||||
}
|
||||
// TODO: 处理通知
|
||||
}
|
||||
@ -39,7 +39,7 @@ func HandleRequest(msgJson []byte) {
|
||||
var request wba.NoticeEventInfo
|
||||
err := json.Unmarshal(msgJson, &request)
|
||||
if err != nil {
|
||||
LOG.ERROR("Unmarshalling request: %v", err)
|
||||
LOG.ERROR("请求事件反序列化失败: %v", err)
|
||||
}
|
||||
// TODO: 处理请求
|
||||
}
|
||||
@ -48,7 +48,7 @@ func HandleMetaEvent(msgJson []byte) {
|
||||
var meta wba.NoticeEventInfo
|
||||
err := json.Unmarshal(msgJson, &meta)
|
||||
if err != nil {
|
||||
LOG.ERROR("Unmarshalling meta: %v", err)
|
||||
LOG.ERROR("元事件反序列化失败: %v", err)
|
||||
}
|
||||
// TODO: 处理元事件
|
||||
}
|
||||
|
26
core/os.go
Normal file
26
core/os.go
Normal 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
|
||||
}
|
@ -155,7 +155,6 @@ func wsAPI(body wba.APIRequestInfo) (Response wba.APIResponseInfo, err error) {
|
||||
return wba.APIResponseInfo{}, fmt.Errorf("请求发送失败: %v", err)
|
||||
}
|
||||
if body.Action == "get_group_list" || body.Action == "get_member_list" {
|
||||
// 处理get_group_list和get_member_list请求,直接返回
|
||||
for {
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
|
283
database/database.go
Normal file
283
database/database.go
Normal 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
|
||||
}
|
40
database/数据库0.1 使用文档.md
Normal file
40
database/数据库0.1 使用文档.md
Normal 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
11
doc.go
@ -9,7 +9,7 @@ const (
|
||||
-h, --help 显示帮助文档
|
||||
-v, --version 显示版本信息
|
||||
-r, --run 运行wind
|
||||
-s, 创建Linux service
|
||||
-s, 创建Linux service(仅Linux)
|
||||
-i, --init 初始化配置文件
|
||||
Website:
|
||||
github.com/Sheyiyuan/ProjectWIND
|
||||
@ -18,4 +18,13 @@ const (
|
||||
-w,
|
||||
`
|
||||
version string = `WIND v0.1.0`
|
||||
|
||||
logo string = `
|
||||
_ __ ____ _ __ ____
|
||||
| | / / / _/ / | / / / __ \
|
||||
| | /| / / / / / |/ / / / / /
|
||||
| |/ |/ / _/ / / /| / / /_/ /
|
||||
|__/|__/ /___/ /_/ |_/ /_____/
|
||||
|
||||
`
|
||||
)
|
||||
|
@ -8,23 +8,28 @@
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Wind</h1>
|
||||
<p>Wind is not dice</p>
|
||||
<p></p>
|
||||
<a href="#" class="button">查看手册</a>
|
||||
<a href="#" class="button">GitHub</a>
|
||||
<div class="container">
|
||||
<div>
|
||||
<h1>Wind</h1>
|
||||
<p>Wind is not dice</p>
|
||||
<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>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<h2>开箱即用</h2>
|
||||
<h2>开箱即用📦</h2>
|
||||
<p>多种部署方式,快速部署于 Windows/Linux/macOS 等主流架构平台。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>内存轻量</h2>
|
||||
<h2>内存轻量🚀</h2>
|
||||
<p>不依赖框架加载,不依赖 Electron,内存占用低至 50-100 MB。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>适配快速</h2>
|
||||
<h2>适配快速💻</h2>
|
||||
<p>使用web ui可视化配置,远程管理,一键部署。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,8 +32,8 @@ p {
|
||||
width: 250px;
|
||||
}
|
||||
.button {
|
||||
background-color: #ee82ee;
|
||||
color: white;
|
||||
background-color: #0fc;
|
||||
color: #555;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
|
2
go.mod
2
go.mod
@ -3,3 +3,5 @@ module ProjectWIND
|
||||
go 1.23.2
|
||||
|
||||
require github.com/gorilla/websocket v1.5.3
|
||||
|
||||
require github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -1,2 +1,4 @@
|
||||
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/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=
|
||||
|
9
main.go
9
main.go
@ -1,10 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"ProjectWIND/core"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var CoreOs = core.GetOS()
|
||||
|
||||
func main() {
|
||||
//如果没有参数,则启动WebUI
|
||||
if len(os.Args) <= 1 {
|
||||
@ -14,6 +17,7 @@ func main() {
|
||||
}
|
||||
cmdArgs := os.Args[1:]
|
||||
if cmdArgs[0] == "-h" || cmdArgs[0] == "--help" {
|
||||
fmt.Printf(logo)
|
||||
fmt.Printf("%v\n", helpDoc)
|
||||
return
|
||||
}
|
||||
@ -29,7 +33,8 @@ func main() {
|
||||
}
|
||||
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
|
||||
}
|
||||
if cmdArgs[0] == "-s" || cmdArgs[0] == "--service" {
|
||||
@ -43,6 +48,6 @@ func main() {
|
||||
startProtocol()
|
||||
return
|
||||
}
|
||||
fmt.Println("Invalid command.")
|
||||
fmt.Println("未知命令,请使用-h查看帮助。")
|
||||
return
|
||||
}
|
||||
|
17
readme.md
17
readme.md
@ -23,4 +23,21 @@ TODO:
|
||||
|
||||
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` 项目团队的开源贡献,使得我们的项目开发能够借助这些优秀的工具快速推进,为用户带来更好的体验。
|
||||
|
||||
---
|
||||
后面没有了,开发者很懒,什么都没写。
|
71
utils.go
71
utils.go
@ -22,7 +22,7 @@ func initCore() string {
|
||||
|
||||
err := checkAndUpdateConfig("./data/core.json")
|
||||
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"))
|
||||
@ -30,24 +30,24 @@ func initCore() string {
|
||||
if os.IsNotExist(err) {
|
||||
file, err := os.Create(logFile)
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to create log file: %v", err)
|
||||
LOG.FATAL("初始化时,创建日志文件失败: %v", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close log file: %v", err)
|
||||
LOG.FATAL("无法关闭日志文件: %v", err)
|
||||
}
|
||||
}(file)
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to open log file: %v", err)
|
||||
LOG.FATAL("初始化时,无法打开日志文件: %v", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close log file: %v", err)
|
||||
LOG.FATAL("无法关闭日志文件: %v", err)
|
||||
}
|
||||
}(file)
|
||||
|
||||
@ -63,7 +63,7 @@ func checkAndUpdateConfig(configPath string) error {
|
||||
// 如果不存在,则创建该文件夹
|
||||
err := os.Mkdir("./data/", 0755)
|
||||
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")
|
||||
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) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close core.json file: %v", err)
|
||||
LOG.FATAL("关闭 ./data/core.json 配置文件失败: %v", err)
|
||||
}
|
||||
}(file)
|
||||
}
|
||||
@ -99,7 +99,7 @@ func checkAndUpdateConfig(configPath string) error {
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close core.json file: %v", err)
|
||||
LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
|
||||
}
|
||||
}(file)
|
||||
|
||||
@ -140,19 +140,19 @@ func checkAndUpdateConfig(configPath string) error {
|
||||
// 将格式化后的JSON字符串写入文件
|
||||
file, err = os.Create("./data/core.json")
|
||||
if err != nil {
|
||||
LOG.FATAL("Error creating core.json file:%v", err)
|
||||
LOG.FATAL("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close core.json file: %v", err)
|
||||
LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
|
||||
}
|
||||
}(file)
|
||||
|
||||
_, err = file.Write(formattedJSON)
|
||||
if err != nil {
|
||||
LOG.FATAL("Error writing to core.json file: %v", err)
|
||||
LOG.FATAL("初始化时,写入 ./data/core.json 配置文件失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -169,24 +169,43 @@ func checkAndUpdateConfig(configPath string) error {
|
||||
|
||||
err = checkDataFolderExistence("./data/app/")
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to create app folder: %v", err)
|
||||
LOG.FATAL("创建应用文件夹 ./data/app/ 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
err = checkDataFolderExistence("./data/images/")
|
||||
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
|
||||
}
|
||||
err = checkDataFolderExistence("./data/database/")
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to create database folder: %v", err)
|
||||
LOG.FATAL("创建数据库文件夹 ./data/database/ 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
err = checkDataFolderExistence("./data/log/")
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to create log folder: %v", err)
|
||||
LOG.FATAL("创建日志文件夹 ./data/log/ 失败: %v", err)
|
||||
return err
|
||||
}
|
||||
err = checkDataFolderExistence("./data/app/configs/")
|
||||
if err != nil {
|
||||
LOG.FATAL("创建应用配置文件夹 ./data/app/configs/ 失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -201,12 +220,12 @@ func startWebUI() {
|
||||
// 打开日志文件
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to open log file: %v", err)
|
||||
LOG.FATAL("打开日志文件失败: %v", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close log file: %v", err)
|
||||
LOG.FATAL("无法关闭日志文件: %v", err)
|
||||
}
|
||||
}(file)
|
||||
// 设置日志输出到文件
|
||||
@ -228,12 +247,12 @@ func registerService() {
|
||||
// 打开日志文件
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to create log file: %v", err)
|
||||
LOG.FATAL("无法打开日志文件: %v", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close log file: %v", err)
|
||||
LOG.FATAL("无法关闭日志文件: %v", err)
|
||||
}
|
||||
}(file)
|
||||
// 设置日志输出到文件
|
||||
@ -250,12 +269,12 @@ func startProtocol() {
|
||||
// 打开日志文件
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to create log file: %v", err)
|
||||
LOG.FATAL("无法打开日志文件: %v", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close log file: %v", err)
|
||||
LOG.FATAL("无法关闭日志文件: %v", err)
|
||||
}
|
||||
}(file)
|
||||
// 设置日志输出到文件
|
||||
@ -266,19 +285,19 @@ func startProtocol() {
|
||||
var config typed.CoreConfigInfo
|
||||
file, err = os.Open("./data/core.json")
|
||||
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) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LOG.FATAL("Failed to close core.json file: %v", err)
|
||||
LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
|
||||
}
|
||||
}(file)
|
||||
|
||||
decoder := json.NewDecoder(file)
|
||||
err = decoder.Decode(&config)
|
||||
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
|
||||
@ -290,7 +309,7 @@ func startProtocol() {
|
||||
err = core.WebSocketHandler(protocolAddr, token)
|
||||
if err != nil {
|
||||
// 如果发生错误,记录错误并退出程序
|
||||
LOG.FATAL("Failed to start WebSocket link program: %v", err)
|
||||
LOG.FATAL("连接协议时,启动 WebSocket 处理程序失败: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
227
wba/readme.md
Normal file
227
wba/readme.md
Normal 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 远程控制模块
|
130
wba/wind.go
130
wba/wind.go
@ -1,13 +1,11 @@
|
||||
package wba
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type APP interface {
|
||||
Get() AppInfo
|
||||
Run(cmd string, args []string, msg MessageEventInfo) error
|
||||
Init(api WindAPI) error
|
||||
}
|
||||
|
||||
@ -29,113 +27,132 @@ type WindAPI interface {
|
||||
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()
|
||||
LogWith(level string, log string, args ...interface{})
|
||||
Log(log string, args ...interface{})
|
||||
}
|
||||
|
||||
type AppInfo struct {
|
||||
name string
|
||||
version string
|
||||
author string
|
||||
description string
|
||||
namespace string
|
||||
webUrl string
|
||||
license string
|
||||
appType string
|
||||
rule string
|
||||
CmdMap map[string]Cmd
|
||||
Name string
|
||||
Version string
|
||||
Author string
|
||||
Description string
|
||||
Namespace string
|
||||
Homepage string
|
||||
License string
|
||||
AppType string
|
||||
Rule string
|
||||
CmdMap map[string]Cmd
|
||||
MessageEventHandler func(msg MessageEventInfo)
|
||||
NoticeEventHandler func(msg NoticeEventInfo)
|
||||
RequestEventHandler func(msg RequestEventInfo)
|
||||
MetaEventHandler func(msg MetaEventInfo)
|
||||
ScheduledTasks map[string]ScheduledTaskInfo
|
||||
API map[string]interface{}
|
||||
}
|
||||
|
||||
func (ei AppInfo) Get() AppInfo {
|
||||
return ei
|
||||
func (ai AppInfo) Get() AppInfo {
|
||||
return ai
|
||||
}
|
||||
|
||||
func (ei *AppInfo) Run(cmd string, args []string, msg MessageEventInfo) 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 {
|
||||
func (ai *AppInfo) Init(api WindAPI) error {
|
||||
Wind = api
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ei *AppInfo) AddCmd(name string, cmd Cmd) error {
|
||||
ei.CmdMap[name] = cmd
|
||||
return nil
|
||||
func (ai *AppInfo) AddCmd(name string, cmd Cmd) {
|
||||
ai.CmdMap[name] = cmd
|
||||
}
|
||||
|
||||
func (ai *AppInfo) AddNoticeEventHandler(ScheduledTask ScheduledTaskInfo) {
|
||||
ai.ScheduledTasks[ScheduledTask.Name] = ScheduledTask
|
||||
}
|
||||
|
||||
type AppInfoOption func(ei *AppInfo)
|
||||
|
||||
func WithName(name string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.name = name
|
||||
ei.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithVersion(version string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.version = version
|
||||
ei.Version = version
|
||||
}
|
||||
}
|
||||
|
||||
func WithAuthor(author string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.author = author
|
||||
ei.Author = author
|
||||
}
|
||||
}
|
||||
|
||||
func WithDescription(description string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.description = description
|
||||
ei.Description = description
|
||||
}
|
||||
}
|
||||
|
||||
func WithNamespace(namespace string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.namespace = namespace
|
||||
ei.Namespace = namespace
|
||||
}
|
||||
}
|
||||
|
||||
func WithWebUrl(webUrl string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.webUrl = webUrl
|
||||
ei.Homepage = webUrl
|
||||
}
|
||||
}
|
||||
|
||||
func WithLicense(license string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.license = license
|
||||
ei.License = license
|
||||
}
|
||||
}
|
||||
|
||||
func WithAppType(appType string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.appType = appType
|
||||
ei.AppType = appType
|
||||
}
|
||||
}
|
||||
|
||||
func WithRule(rule string) AppInfoOption {
|
||||
return func(ei *AppInfo) {
|
||||
ei.rule = fmt.Sprintf("rule_%s", rule)
|
||||
ei.Rule = fmt.Sprintf("rule_%s", rule)
|
||||
}
|
||||
}
|
||||
|
||||
func NewApp(opts ...AppInfoOption) AppInfo {
|
||||
Ext := AppInfo{
|
||||
name: "Wind",
|
||||
version: "v1.0.0",
|
||||
author: "Wind",
|
||||
description: "A simple and easy-to-use bot framework",
|
||||
namespace: "wind",
|
||||
webUrl: "https://github.com/Sheyiyuan/wind_app_model",
|
||||
license: "MIT",
|
||||
appType: "fun",
|
||||
rule: "none",
|
||||
Name: "Wind",
|
||||
Version: "v1.0.0",
|
||||
Author: "Wind",
|
||||
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),
|
||||
}
|
||||
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 {
|
||||
NAME string
|
||||
DESC string
|
||||
@ -214,7 +240,7 @@ type MetaEventInfo struct {
|
||||
|
||||
type FileInfo struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Name string `json:"Name,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Busid int64 `json:"bucket,omitempty"`
|
||||
}
|
||||
@ -233,7 +259,7 @@ type SenderInfo struct {
|
||||
|
||||
type AnonymousInfo struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Name string `json:"Name,omitempty"`
|
||||
Flag string `json:"flag,omitempty"`
|
||||
}
|
||||
|
||||
@ -363,9 +389,10 @@ type HonorInfo struct {
|
||||
UserId int64 `json:"user_id,omitempty"`
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Description string `json:"Description,omitempty"`
|
||||
}
|
||||
|
||||
// SegmentInfo 消息段
|
||||
type SegmentInfo struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Data SegmentDataInfo `json:"data,omitempty"`
|
||||
@ -388,4 +415,11 @@ type SegmentDataInfo struct {
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user