Compare commits

...

32 Commits

Author SHA1 Message Date
c05cdddb1e Merge pull request 'modify:更新了readme' (#9) from SheyiyuanDev into dev
Reviewed-on: ProjectWIND/ProjectWIND#9
2025-04-22 15:06:12 +08:00
Sheyiyuan
2141cd9840 modify:更新了readme 2025-04-22 15:04:26 +08:00
Sheyiyuan
33e0d1048c modify:同步数据库日志更正 2025-04-22 13:50:32 +08:00
Sheyiyuan
9e47650032 add:完善指令处理模块 2025-04-22 12:28:44 +08:00
Sheyiyuan
995fba34b4 modfiy:rebuild package wba 2025-04-04 10:33:35 +08:00
Sheyiyuan
93538afaeb modify:底层接口变动 2025-04-01 01:20:35 +08:00
Sheyiyuan
c9d36abe4a Merge branch 'bhc' into dev 2025-03-05 16:49:59 +08:00
5e3fc91cc2 Merge branch 'dev' into bhc 2025-03-05 16:47:49 +08:00
5fd2e7bbdd refactor(database): 重构数据库变量设置和获取方法,增加不安全方法支持,增加公开数据库的创建支持 2025-03-05 16:27:12 +08:00
Sheyiyuan
ea5ac718b7 Merge branch 'bhc' into dev 2025-03-03 16:23:15 +08:00
5a36235774 添加权限验证逻辑及参数命名优化 2025-03-03 16:17:31 +08:00
4cc3808ab8 Merge remote-tracking branch 'origin/dev' into bhc 2025-03-03 16:03:31 +08:00
Sheyiyuan
5659914298 合并分支SheyiyuanDev 2025-03-03 15:37:59 +08:00
Sheyiyuan
8e604fd397 Merge branch 'SheyiyuanDev' into dev 2025-03-03 14:42:48 +08:00
cdc0316c52 解决了提交带来的bug 2025-03-03 14:38:17 +08:00
2452df4da6 Merge branch 'dev' of http://sheyiyuan.cn:3000/ProjectWIND/ProjectWIND into dev 2025-03-03 14:32:46 +08:00
0a3a260eda 将数据库api导入到js插件 2025-03-03 14:21:54 +08:00
Sheyiyuan
5a5c3a4c48 优化了插件加载管理,修改了部分插件规范 2025-03-03 14:20:09 +08:00
8a49ded455 将数据库api导入到js插件 2025-03-03 14:13:48 +08:00
Sheyiyuan
c63f4c2dd8 修改了readme文件 2025-03-02 11:34:50 +08:00
Sheyiyuan
944fadc688 修改了readme文件 2025-03-02 11:31:59 +08:00
Sheyiyuan
7eab31081c 重新修改了协议处理 2025-03-02 11:25:49 +08:00
dfc10927b3 Merge branch 'dev' of http://sheyiyuan.cn:3000/ProjectWIND/ProjectWIND into bhc 2025-02-26 16:29:57 +08:00
308560facd 添加数据库master权限支持并优化权限验证逻辑 2025-02-26 16:29:25 +08:00
Sheyiyuan
753c6dc0ca 为Windows添加了js插件基础支持 2025-02-26 09:10:27 +08:00
Sheyiyuan
c2382c8a31 添加了js插件基础支持 2025-02-26 09:07:39 +08:00
e22b2f980d Merge pull request 'bhc' (#3) from bhc into dev
Reviewed-on: ProjectWIND/ProjectWIND#3
2025-02-25 10:43:20 +08:00
bb599fad28 添加了数据库和配置项的api 2025-02-25 08:49:59 +08:00
d5fedc873b Merge branch 'dev' of http://sheyiyuan.cn:3000/ProjectWIND/ProjectWIND into bhc 2025-02-24 10:09:30 +08:00
5ff2e78a86 feat: 添加数据库模块的读写操作api及配置项获取 2025-02-23 16:42:27 +08:00
Sheyiyuan
ab7f30660d 更新了接口格式,添加了多账号处理 2025-02-23 16:10:51 +08:00
Sheyiyuan
f7905c7f56 修改了部分api表述 2025-02-13 13:32:24 +08:00
33 changed files with 4024 additions and 1729 deletions

8
.gitignore vendored
View File

@ -1,8 +1,6 @@
/tmp
/scripts
/ProjectWIND
/data
tmp/
data/
**/.DS_Store
/app_demo/
.idea/
.vscode/
ProjectWIND

View File

@ -1,40 +1,157 @@
package LOG
import (
"context"
"fmt"
"github.com/cloudwego/hertz/pkg/common/hlog"
"io"
"log"
"runtime"
)
func DEBUG(text string, msg ...interface{}) {
pc, file, line, ok := runtime.Caller(3)
if !ok {
pc, file, line, ok = runtime.Caller(2)
func Trace(text string, msg ...interface{}) {
var pc uintptr
var file string
var line int
var ok bool
// 从第 2 层开始循环查找调用栈,直到找不到调用信息为止
for i := 2; ; i++ {
pc, file, line, ok = runtime.Caller(i)
if !ok || line <= 0 {
pc, file, line, ok = runtime.Caller(i - 1)
break
}
}
if ok {
funcName := runtime.FuncForPC(pc).Name()
log.Printf("[DEBUG] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...))
} else {
log.Printf("[DEBUG] %s\n", fmt.Sprintf(text, msg...))
}
log.Printf("[Trace] [%s:%d %s()] %s\n", file, line, funcName, fmt.Sprintf(text, msg...))
}
func INFO(text string, msg ...interface{}) {
msgText := fmt.Sprintf(text, msg...)
log.Println("[INFO] ", msgText)
func Debug(text string, msg ...interface{}) {
log.Printf("[Debug] %s\n", fmt.Sprintf(text, msg...))
}
func WARN(text string, msg ...interface{}) {
func Info(text string, msg ...interface{}) {
msgText := fmt.Sprintf(text, msg...)
log.Println("[WARN] ", msgText)
log.Println("[Info] ", msgText)
}
func ERROR(text string, msg ...interface{}) {
func Notice(text string, msg ...interface{}) {
msgText := fmt.Sprintf(text, msg...)
log.Println("[ERROR] ", msgText)
log.Println("[Notice]", msgText)
}
func FATAL(text string, msg ...interface{}) {
func Warn(text string, msg ...interface{}) {
msgText := fmt.Sprintf(text, msg...)
log.Fatalln("[FATAL] ", msgText)
log.Println("[Warn] ", msgText)
}
func Error(text string, msg ...interface{}) {
msgText := fmt.Sprintf(text, msg...)
log.Println("[Error] ", msgText)
}
func Fatal(text string, msg ...interface{}) {
msgText := fmt.Sprintf(text, msg...)
log.Fatalln("[Fatal] ", msgText)
}
// CustomLogger 是一个实现了 hlog.Logger 接口的自定义日志记录器
type CustomLogger struct{}
func (c *CustomLogger) Trace(v ...interface{}) {
Trace(fmt.Sprint(v...))
}
func (c *CustomLogger) Debug(v ...interface{}) {
Debug(fmt.Sprint(v...))
}
func (c *CustomLogger) Info(v ...interface{}) {
Info(fmt.Sprint(v...))
}
func (c *CustomLogger) Notice(v ...interface{}) {
Info(fmt.Sprint(v...))
}
func (c *CustomLogger) Warn(v ...interface{}) {
Warn(fmt.Sprint(v...))
}
func (c *CustomLogger) Error(v ...interface{}) {
Error(fmt.Sprint(v...))
}
func (c *CustomLogger) Fatal(v ...interface{}) {
Fatal(fmt.Sprint(v...))
}
func (c *CustomLogger) CtxTracef(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) CtxDebugf(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) CtxInfof(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) CtxNoticef(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) CtxWarnf(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) CtxErrorf(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) CtxFatalf(ctx context.Context, format string, v ...interface{}) {
}
func (c *CustomLogger) SetLevel(level hlog.Level) {
}
func (c *CustomLogger) SetOutput(writer io.Writer) {
}
// Tracef 实现 hlog.Logger 接口的 Tracef 方法
func (c *CustomLogger) Tracef(format string, args ...interface{}) {
Trace(format, args...)
}
// Debugf 实现 hlog.Logger 接口的 Debugf 方法
func (c *CustomLogger) Debugf(format string, args ...interface{}) {
Debug(format, args...)
}
// Infof 实现 hlog.Logger 接口的 Infof 方法
func (c *CustomLogger) Infof(format string, args ...interface{}) {
Info(format, args...)
}
// Warnf 实现 hlog.Logger 接口的 Warnf 方法
func (c *CustomLogger) Warnf(format string, args ...interface{}) {
Warn(format, args...)
}
// Errorf 实现 hlog.Logger 接口的 Errorf 方法
func (c *CustomLogger) Errorf(format string, args ...interface{}) {
Error(format, args...)
}
// Fatalf 实现 hlog.Logger 接口的 Fatalf 方法
func (c *CustomLogger) Fatalf(format string, args ...interface{}) {
Fatal(format, args...)
}
func (c *CustomLogger) Noticef(format string, args ...interface{}) {
Info(format, args...)
}

View File

@ -1,754 +0,0 @@
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"crypto/rand"
"fmt"
)
type apiInfo struct{}
//一、Protocol模块
/*
关于Protocol模块的说明
1.所有API请求按照OneBot11标准使用JSON格式进行数据交换api命名为由原文档中蛇形命名法改为双驼峰命名法
2.无响应的API请求使用ws协议处理有响应的API需添加echo字段
3.wind会从配置文件中读取API请求的url请确保正确填写
*/
//1.无响应API,使用ws协议处理
// SendMsg 发送消息(自动判断消息类型)
func (a *apiInfo) SendMsg(msg wba.MessageEventInfo, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageType := msg.MessageType
messageData.Action = "send_msg"
switch messageType {
case "private":
{
messageData.Params.UserId = msg.UserId
break
}
case "group":
{
messageData.Params.GroupId = msg.GroupId
break
}
default:
{
LOG.ERROR("发送消息(SendMsg)时,消息类型错误: %v", messageType)
}
}
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("发送消息时,发送失败: %v", err)
return
}
LOG.INFO("发送消息(SendMsg)(至:%v-%v:%v-%v):%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, message)
return
}
// SendPrivateMsg 发送私聊消息
func (a *apiInfo) SendPrivateMsg(msg wba.MessageEventInfo, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_private_msg"
messageData.Params.UserId = msg.UserId
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("发送消息(SendPrivateMsg)时,发送失败: %v", err)
return
}
LOG.INFO("发送消息(SendPrivateMsg)(至:%v-%v:%v-%v):%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, message)
return
}
// SendGroupMsg 发送群消息
func (a *apiInfo) SendGroupMsg(msg wba.MessageEventInfo, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_group_msg"
messageData.Params.GroupId = msg.GroupId
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("发送消息(SendGroupMsg)时,发送失败: %v", err)
return
}
LOG.INFO("发送消息(SendGroupMsg)(至:%v-%v:%v-%v):%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, message)
return
}
// DeleteMsg 撤回消息
func (a *apiInfo) DeleteMsg(msg wba.MessageEventInfo) {
// 构建删除消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "delete_msg"
messageData.Params.MessageId = msg.MessageId
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("撤回消息(DeleteMsg)时,发送失败: %v", err)
return
}
LOG.INFO("撤回消息(DeleteMsg):[id:%v]%v", msg.MessageId, msg.RawMessage)
return
}
// SendLike 发送赞
func (a *apiInfo) SendLike(userId int64, times int) {
// 构建发送赞的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_like"
messageData.Params.UserId = userId
messageData.Params.Times = times
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("发送赞(SendLike)时,发送失败: %v", err)
return
}
LOG.INFO("发送赞(SendLike)(至:%v):%v", userId, times)
return
}
// SetGroupKick 将指定用户移出群聊(需要群主或管理员权限)
func (a *apiInfo) SetGroupKick(groupId int64, userId int64, rejectAddRequest bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_kick"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.RejectAddRequest = rejectAddRequest
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("移出群聊(SetGroupKick)时,发送失败: %v", err)
return
}
LOG.INFO("移出群聊(SetGroupKick)(从:%v-%v):%v", groupId, userId, rejectAddRequest)
return
}
// SetGroupBan 将指定用户禁言(需要群主或管理员权限)
func (a *apiInfo) SetGroupBan(groupId int64, userId int64, duration int32) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_ban"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.Duration = duration
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("禁言群成员(SetGroupBan)时,执行失败: %v", err)
return
}
LOG.INFO("禁言群成员(SetGroupBan)(在:%v-%v):%v", groupId, userId, duration)
return
}
// SetGroupWholeBan 设置全员禁言(需要群主或管理员权限)
func (a *apiInfo) SetGroupWholeBan(groupId int64, enable bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_whole_ban"
messageData.Params.GroupId = groupId
messageData.Params.Enable = enable
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("设置全员禁言(SetGroupWholeBan)时,执行失败: %v", err)
return
}
LOG.INFO("设置全员禁言(SetGroupWholeBan)(在:%v):%v", groupId, enable)
return
}
// SetGroupAdmin 设置群管理员(需要群主权限)
func (a *apiInfo) SetGroupAdmin(groupId int64, userId int64, enable bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_admin"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.Enable = enable
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("设置群管理员(SetGroupAdmin)时,执行失败: %v", err)
return
}
LOG.INFO("设置群管理员(SetGroupAdmin)(在:%v-%v):%v", groupId, userId, enable)
return
}
// SetGroupCard 设置群名片(需要群主或管理员权限)
func (a *apiInfo) SetGroupCard(groupId int64, userId int64, card string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_card"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.Card = card
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("设置群名片(SetGroupCard)时,执行失败: %v", err)
return
}
LOG.INFO("设置群名片(SetGroupCard)(在:%v-%v):%v", groupId, userId, card)
return
}
// SetGroupName 设置群名称(可能需要群主或管理员权限)
func (a *apiInfo) SetGroupName(groupId int64, groupName string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_name"
messageData.Params.GroupId = groupId
messageData.Params.GroupName = groupName
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("设置群名称(SetGroupName)时,执行失败: %v", err)
return
}
LOG.INFO("设置群名称(SetGroupName)(在:%v):%v", groupId, groupName)
return
}
// SetGroupLeave 退出群聊
func (a *apiInfo) SetGroupLeave(groupId int64, isDismiss bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_leave"
messageData.Params.GroupId = groupId
messageData.Params.IsDismiss = isDismiss
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("退出群聊(SetGroupLeave)时,执行失败: %v", err)
return
}
LOG.INFO("退出群聊(SetGroupLeave)(在:%v):%v", groupId, isDismiss)
return
}
// SetGroupSpecialTitle 设置群专属头衔(需要群主权限)
func (a *apiInfo) SetGroupSpecialTitle(groupId int64, userId int64, specialTitle string, duration int32) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_special_title"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.SpecialTitle = specialTitle
messageData.Params.Duration = duration
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("设置群特殊头衔(SetGroupSpecialTitle)时,执行失败: %v", err)
return
}
LOG.INFO("设置群特殊头衔(SetGroupSpecialTitle)(在:%v-%v):%v-%v", groupId, userId, specialTitle, duration)
return
}
// SetFriendAddRequest 处理加好友请求
func (a *apiInfo) SetFriendAddRequest(flag string, approve bool, remark string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_friend_add_request"
messageData.Params.Flag = flag
messageData.Params.Approve = approve
messageData.Params.Remark = remark
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("处理加好友请求(SetFriendAddRequest)时,执行失败: %v", err)
return
}
LOG.INFO("处理加好友请求(SetFriendAddRequest)(在:%v):%v-%v-%v", flag, approve, remark)
return
}
// SetGroupAddRequest 处理加群请求/邀请
func (a *apiInfo) SetGroupAddRequest(flag string, subType string, approve bool, reason string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_add_request"
messageData.Params.Flag = flag
messageData.Params.SubType = subType
messageData.Params.Approve = approve
messageData.Params.Reason = reason
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("处理加群请求/邀请(SetGroupAddRequest)时,执行失败: %v", err)
return
}
LOG.INFO("处理加群请求/邀请(SetGroupAddRequest)(在:%v-%v-%v):%v", flag, subType, approve, reason)
return
}
// SetRestart 重启
func (a *apiInfo) SetRestart(delay int32) {
var messageData wba.APIRequestInfo
messageData.Action = "set_restart"
messageData.Params.Delay = delay
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("设置重启(SetRestart)时,执行失败: %v", err)
return
}
LOG.INFO("设置重启(SetRestart):%v", delay)
return
}
// CleanCache 清理缓存
func (a *apiInfo) CleanCache() {
var messageData wba.APIRequestInfo
messageData.Action = "clean_cache"
_, err := wsAPI(messageData)
if err != nil {
LOG.ERROR("清理缓存(CleanCache)时,执行失败: %v", err)
return
}
LOG.INFO("清理缓存(CleanCache)")
return
}
// 2.有响应API需添加echo字段
// GetLoginInfo 获取登录信息
func (a *apiInfo) GetLoginInfo() (Response wba.APIResponseInfo) {
LOG.INFO("获取登录信息(GetLoginInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_login_info"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取登录信息(GetLoginInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取登录信息(GetLoginInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetVersionInfo 获取协议信息
func (a *apiInfo) GetVersionInfo() (Response wba.APIResponseInfo) {
LOG.INFO("获取协议信息(GetVersionInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_version_info"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取协议信息(GetVersionInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取登录信息(GetVersionInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetMsg 获取消息
func (a *apiInfo) GetMsg(messageId int32) (Response wba.APIResponseInfo) {
LOG.INFO("获取消息(GetMsg)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_msg"
messageData.Params.MessageId = messageId
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取消息(GetMsg)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取消息(GetMsg)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetForwardMsg 获取合并转发消息
func (a *apiInfo) GetForwardMsg(id string) (Response wba.APIResponseInfo) {
LOG.INFO("获取合并转发消息(GetForwardMsg)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_forward_msg"
messageData.Params.Id = id
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取合并转发消息(GetForwardMsg)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取合并转发消息(GetForwardMsg)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetStrangerInfo 获取陌生人信息
func (a *apiInfo) GetStrangerInfo(userId int64, noCache bool) (Response wba.APIResponseInfo) {
LOG.INFO("获取陌生人信息(GetStrangerInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_stranger_info"
messageData.Params.UserId = userId
messageData.Params.NoCache = noCache
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取陌生人信息(GetStrangerInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取陌生人信息(GetStrangerInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetFriendList 获取好友列表
func (a *apiInfo) GetFriendList() (Response wba.APIResponseInfo) {
LOG.INFO("获取好友列表(GetFriendList)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_friend_list"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取好友列表(GetFriendList)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取好友列表(GetFriendList)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupList 获取群列表
func (a *apiInfo) GetGroupList() (Response wba.APIResponseInfo) {
LOG.INFO("获取群列表(GetGroupList)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_list"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取群列表(GetGroupList)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取群列表(GetGroupList)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupInfo 获取群信息
func (a *apiInfo) GetGroupInfo(groupId int64, noCache bool) (Response wba.APIResponseInfo) {
LOG.INFO("获取群信息(GetGroupInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_info"
messageData.Params.GroupId = groupId
messageData.Params.NoCache = noCache
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取群信息(GetGroupInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取群信息(GetGroupInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupMemberInfo 获取群成员信息
func (a *apiInfo) GetGroupMemberInfo(groupId int64, userId int64, noCache bool) (Response wba.APIResponseInfo) {
LOG.INFO("获取群成员信息(GetGroupMemberInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_member_info"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.NoCache = noCache
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取群成员信息(GetGroupMemberInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取群成员信息(GetGroupMemberInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupMemberList 获取群成员列表
func (a *apiInfo) GetGroupMemberList(groupId int64) (Response wba.APIResponseInfo) {
LOG.INFO("获取群成员列表(GetGroupMemberList)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_member_list"
messageData.Params.GroupId = groupId
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取群成员列表(GetGroupMemberList)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取群成员列表(GetGroupMemberList)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupHonorInfo 获取群荣誉信息
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.Type = Type
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取群荣誉信息(GetGroupHonorInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取群荣誉信息(GetGroupHonorInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetCookies 获取Cookies
func (a *apiInfo) GetCookies(domain string) (Response wba.APIResponseInfo) {
LOG.INFO("获取Cookies(GetCookies)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_cookies"
messageData.Params.Domain = domain
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取Cookies(GetCookies)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取Cookies(GetCookies)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetCSRFToken 获取CSRF Token
func (a *apiInfo) GetCSRFToken() (Response wba.APIResponseInfo) {
LOG.INFO("获取CSRF Token(GetCSRFToken)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_csrf_token"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取CSRF Token(GetCSRFToken)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取CSRF Token(GetCSRFToken)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetCredentials 获取登录令牌
func (a *apiInfo) GetCredentials(domain string) (Response wba.APIResponseInfo) {
LOG.INFO("获取登录令牌(GetCredentials)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_credentials"
messageData.Params.Domain = domain
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取登录令牌(GetCredentials)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取登录令牌(GetCredentials)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetRecord 获取语音
func (a *apiInfo) GetRecord(file string, outFormat string) (Response wba.APIResponseInfo) {
LOG.INFO("获取语音(GetRecord)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_record"
messageData.Params.File = file
messageData.Params.OutFormat = outFormat
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取语音(GetRecord)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取语音(GetRecord)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetImage 获取图片
func (a *apiInfo) GetImage(file string) (Response wba.APIResponseInfo) {
LOG.INFO("获取图片(GetImage)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_image"
messageData.Params.File = file
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取图片(GetImage)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取图片(GetImage)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// CanSendImage 检查是否可以发送图片
func (a *apiInfo) CanSendImage() (Response wba.APIResponseInfo) {
LOG.INFO("检查是否可以发送图片(CanSendImage)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "can_send_image"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("检查是否可以发送图片(CanSendImage)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("检查是否可以发送图片(CanSendImage)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// CanSendRecord 检查是否可以发送语音
func (a *apiInfo) CanSendRecord() (Response wba.APIResponseInfo) {
LOG.INFO("检查是否可以发送语音(CanSendRecord)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "can_send_record"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("检查是否可以发送语音(CanSendRecord)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("检查是否可以发送语音(CanSendRecord)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetStatus 获取状态
func (a *apiInfo) GetStatus() (Response wba.APIResponseInfo) {
LOG.INFO("获取状态(GetStatus)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_status"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.ERROR("获取状态(GetStatus)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.ERROR("获取状态(GetStatus)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
//二、LOG模块
/*
关于LOG模块的说明
1.日志模块使用go-logging库日志级别分为DEBUGINFOWARNERROR
2.日志模块提供LogWith方法可以自定义日志级别调用级别为DEBUG时会打印输出调用者的文件名函数名行号
3.日志模块提供Log方法默认日志级别为INFO
*/
func (a *apiInfo) LogWith(level string, content string, args ...interface{}) {
switch level {
case "DEBUG":
LOG.DEBUG(content, args...)
return
case "WARN":
LOG.WARN(content, args...)
return
case "ERROR":
LOG.ERROR(content, args...)
return
default:
LOG.INFO(content, args...)
return
}
}
func (a *apiInfo) Log(content string, args ...interface{}) {
LOG.INFO(content, args...)
}
//database模块
//TODO: 数据库模块待实现
// 文件管理模块
//TODO: 文件管理模块待实现
//终端连接模块
//TODO: 终端模块待实现
//核心信息调用模块
var AppApi apiInfo
func GenerateUUID() (string, error) {
uuid := make([]byte, 16)
_, err := rand.Read(uuid)
if err != nil {
return "", err
}
// 设置UUID版本号版本4将第6字节的高4位设置为0100
uuid[6] = (uuid[6] & 0x0F) | 0x40
// 设置UUID变体RFC 4122规范定义的变体将第8字节的高4位设置为10
uuid[8] = (uuid[8] & 0x3F) | 0x80
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}

207
core/api_wsd.go Normal file
View File

@ -0,0 +1,207 @@
package core
import (
"ProjectWIND/database"
"ProjectWIND/wba"
"fmt"
)
//database模块
// //数据库部分允许字符串变量的读写操作,允许读取配置项操作
type databaseInfo struct{}
func (dbi *databaseInfo) varSet(app wba.AppInfo, datamap string, unit string, id string, key string, value string) {
database.Set(app.AppKey.Name, datamap, unit, id, key, value)
}
func (dbi *databaseInfo) SetUserVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string, value string) {
id := fmt.Sprintf("%d", msg.UserId)
dbi.varSet(app, app.AppKey.Name, "user", id, key, value)
}
func (dbi *databaseInfo) SetGroupVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string, value string) {
var id string
if msg.MessageType == "group" {
id = "group_" + fmt.Sprintf("%d", msg.GroupId)
}
if msg.MessageType == "private" {
id = "user_" + fmt.Sprintf("%d", msg.UserId)
}
dbi.varSet(app, app.AppKey.Name, "group", id, key, value)
}
func (dbi *databaseInfo) SetOutUserVariable(app wba.AppInfo, datamap string, msg wba.MessageEventInfo, key string, value string) {
id := fmt.Sprintf("%d", msg.UserId)
dbi.varSet(app, datamap, "user", id, key, value)
}
func (dbi *databaseInfo) SetOutGroupVariable(app wba.AppInfo, datamap string, msg wba.MessageEventInfo, key string, value string) {
var id string
if msg.MessageType == "group" {
id = "group_" + fmt.Sprintf("%d", msg.GroupId)
}
if msg.MessageType == "private" {
id = "user_" + fmt.Sprintf("%d", msg.UserId)
}
dbi.varSet(app, datamap, "group", id, key, value)
}
func (dbi *databaseInfo) UnsafelySetUserVariable(app wba.AppInfo, id string, key string, value string) {
dbi.varSet(app, app.AppKey.Name, "user", id, key, value)
}
func (dbi *databaseInfo) UnsafelySetGroupVariable(app wba.AppInfo, id string, key string, value string) {
dbi.varSet(app, app.AppKey.Name, "group", id, key, value)
}
func (dbi *databaseInfo) UnsafelySetGlobalVariable(app wba.AppInfo, id string, key string, value string) {
dbi.varSet(app, app.AppKey.Name, "global", id, key, value)
}
func (dbi *databaseInfo) UnsafelySetOutUserVariable(app wba.AppInfo, datamap string, id string, key string, value string) {
dbi.varSet(app, datamap, "user", id, key, value)
}
func (dbi *databaseInfo) UnsafelySetOutGroupVariable(app wba.AppInfo, datamap string, id string, key string, value string) {
dbi.varSet(app, datamap, "group", id, key, value)
}
func (dbi *databaseInfo) UnsafelySetOutGlobalVariable(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.AppKey.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, msg wba.MessageEventInfo, key string) (string, bool) {
id := fmt.Sprintf("%d", msg.UserId)
return dbi.varGet(app, app.AppKey.Name, "user", id, key)
}
func (dbi *databaseInfo) GetGroupVariable(app wba.AppInfo, msg wba.MessageEventInfo, key string) (string, bool) {
var id string
if msg.MessageType == "group" {
id = "group_" + fmt.Sprintf("%d", msg.GroupId)
}
if msg.MessageType == "private" {
id = "user_" + fmt.Sprintf("%d", msg.UserId)
}
return dbi.varGet(app, app.AppKey.Name, "group", id, key)
}
func (dbi *databaseInfo) GetOutUserVariable(app wba.AppInfo, datamap string, msg wba.MessageEventInfo, key string) (string, bool) {
id := fmt.Sprintf("%d", msg.UserId)
return dbi.varGet(app, datamap, "user", id, key)
}
func (dbi *databaseInfo) GetOutGroupVariable(app wba.AppInfo, datamap string, msg wba.MessageEventInfo, key string) (string, bool) {
var id string
if msg.MessageType == "group" {
id = "group_" + fmt.Sprintf("%d", msg.GroupId)
}
if msg.MessageType == "private" {
id = "user_" + fmt.Sprintf("%d", msg.UserId)
}
return dbi.varGet(app, datamap, "group", id, key)
}
func (dbi *databaseInfo) UnsafelyGetUserVariable(app wba.AppInfo, id string, key string) (string, bool) {
return dbi.varGet(app, app.AppKey.Name, "user", id, key)
}
func (dbi *databaseInfo) UnsafelyGetGroupVariable(app wba.AppInfo, id string, key string) (string, bool) {
return dbi.varGet(app, app.AppKey.Name, "group", id, key)
}
func (dbi *databaseInfo) UnsafelyGetGlobalVariable(app wba.AppInfo, id string, key string) (string, bool) {
return dbi.varGet(app, app.AppKey.Name, "global", id, key)
}
func (dbi *databaseInfo) UnsafelyGetOutUserVariable(app wba.AppInfo, datamap string, id string, key string) (string, bool) {
return dbi.varGet(app, datamap, "user", id, key)
}
func (dbi *databaseInfo) UnsafelyGetOutGroupVariable(app wba.AppInfo, datamap string, id string, key string) (string, bool) {
return dbi.varGet(app, datamap, "group", id, key)
}
func (dbi *databaseInfo) UnsafelyGetOutGlobalVariable(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.AppKey.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.AppKey.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.AppKey.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.AppKey.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.AppKey.Name, datamap, "config", "string_slice", key, true)
if !ok {
return nil, false
}
resSlice, ok := res.([]string)
if !ok {
return nil, false
}
return resSlice, true
}
func (dbi *databaseInfo) UnsafelyCreatePublicDatamap(app wba.AppInfo, datamapId string) {
appName := app.AppKey.Name
database.CreatePublicDatamap(appName, datamapId)
}
var DatabaseApi databaseInfo

952
core/api_wsp.go Normal file
View File

@ -0,0 +1,952 @@
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"crypto/rand"
"fmt"
)
type protocolAPI struct{}
//一、Protocol模块
/*
关于Protocol模块的说明
1.所有API请求按照OneBot11标准使用JSON格式进行数据交换api命名为由原文档中蛇形命名法改为双驼峰命名法
2.无响应的API请求使用ws协议处理有响应的API需添加echo字段
3.wind会从配置文件中读取API请求的url请确保正确填写
*/
//1.无响应API,使用ws协议处理
// UnsafelySendMsg 发送消息(自动判断消息类型)
//
// 注意该API不安全除非你知道你在做什么。
//
// 参数:
//
// - messageType: 消息类型,可选值为 "private" 和 "group
//
// - groupId: 群号当messageType为"group"时必填
//
// - userId: 用户ID当messageType为"private"时必填
//
// - message: 要发送的消息内容,类型为字符串
//
// - autoEscape: 是否自动转义解析消息内容中的CQ码可选值为 true 和 false
func (p *protocolAPI) UnsafelySendMsg(messageType string, groupId int64, userId int64, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_msg"
switch messageType {
case "private":
{
messageData.Params.UserId = userId
break
}
case "group":
{
messageData.Params.GroupId = groupId
break
}
default:
{
LOG.Error("发送消息(UnsafelySendMsg)时,消息类型错误: %v", messageType)
}
}
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("发送消息时,发送失败: %v", err)
return
}
LOG.Info("发送消息(UnsafelySendMsg)(至:%v-%v:%v):%v", messageType, groupId, userId, message)
return
}
// UnsafelySendPrivateMsg 发送私聊消息
//
// 注意该API不安全除非你知道你在做什么。
//
// 参数:
//
// - userId: 用户ID
//
// - message: 要发送的消息内容,类型为字符串
//
// - autoEscape: 是否自动转义解析消息内容中的CQ码可选值为 true 和 false
func (p *protocolAPI) UnsafelySendPrivateMsg(userId int64, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_private_msg"
messageData.Params.UserId = userId
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("发送私聊消息(UnsafelySendPrivateMsg)时,发送失败: %v", err)
return
}
LOG.Info("发送私聊消息(UnsafelySendPrivateMsg)(至:%v):%v", userId, message)
return
}
// UnsafelySendGroupMsg 发送群消息
//
// 注意该API不安全除非你知道你在做什么。
//
// 参数:
//
// - groupId: 群号
//
// - message: 要发送的消息内容,类型为字符串
//
// - autoEscape: 是否自动转义解析消息内容中的CQ码可选值为 true 和 false
func (p *protocolAPI) UnsafelySendGroupMsg(groupId int64, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_group_msg"
messageData.Params.GroupId = groupId
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("发送群消息(UnsafelySendGroupMsg)时,发送失败: %v", err)
return
}
LOG.Info("发送群消息(UnsafelySendGroupMsg)(至:%v):%v", groupId, message)
return
}
// SendMsg 回复消息(自动判断消息类型)
//
// 参数:
//
// - msg: 消息事件信息
//
// - message: 要发送的消息内容,类型为字符串
//
// - autoEscape: 是否自动转义解析消息内容中的CQ码可选值为 true 和 false
func (p *protocolAPI) SendMsg(msg wba.MessageEventInfo, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageType := msg.MessageType
messageData.Action = "send_msg"
switch messageType {
case "private":
{
messageData.Params.UserId = msg.UserId
break
}
case "group":
{
messageData.Params.GroupId = msg.GroupId
break
}
default:
{
LOG.Error("回复消息(SendMsg)时,消息类型错误: %v", messageType)
}
}
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("回复消息时,发送失败: %v", err)
return
}
LOG.Info("回复消息(SendMsg)(至:%v-%v:%v-%v):%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, message)
return
}
// SendPrivateMsg 回复私聊消息
//
// 参数:
//
// - msg: 原始消息事件信息
//
// - message: 要发送的消息内容,类型为字符串
//
// - autoEscape: 是否自动转义,可选值为 true 和 false
func (p *protocolAPI) SendPrivateMsg(msg wba.MessageEventInfo, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_private_msg"
messageData.Params.UserId = msg.UserId
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("回复消息(SendPrivateMsg)时,发送失败: %v", err)
return
}
LOG.Info("回复消息(SendPrivateMsg)(至:%v-%v:%v-%v):%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, message)
return
}
// SendGroupMsg 回复群消息
//
// 参数:
//
// - msg: 原始消息事件信息
//
// - message: 要发送的消息内容,类型为字符串
//
// - autoEscape: 是否自动转义,可选值为 true 和 false
func (p *protocolAPI) SendGroupMsg(msg wba.MessageEventInfo, message string, autoEscape bool) {
// 构建发送消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_group_msg"
messageData.Params.GroupId = msg.GroupId
messageData.Params.Message = message
messageData.Params.AutoEscape = autoEscape
// 发送消息
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("回复消息(SendGroupMsg)时,发送失败: %v", err)
return
}
LOG.Info("回复消息(SendGroupMsg)(至:%v-%v:%v-%v):%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, message)
return
}
// UnsafelyDeleteMsg 撤回消息
//
// 注意该API不安全除非你知道你在做什么。
//
// 参数:
//
// - messageId: 要撤回的消息ID
func (p *protocolAPI) UnsafelyDeleteMsg(messageId int32) {
// 构建删除消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "delete_msg"
messageData.Params.MessageId = messageId
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("撤回消息(UnsafeDeleteMsg)时,发送失败: %v", err)
return
}
LOG.Info("撤回消息(UnsafeDeleteMsg):[id:%v]", messageId)
return
}
// DeleteMsg 撤回消息
//
// 参数:
//
// - msg: 原始消息事件信息
func (p *protocolAPI) DeleteMsg(msg wba.MessageEventInfo) {
// 构建删除消息的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "delete_msg"
messageData.Params.MessageId = msg.MessageId
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("撤回消息(DeleteMsg)时,发送失败: %v", err)
return
}
LOG.Info("撤回消息(DeleteMsg):[id:%v]%v", msg.MessageId, msg.RawMessage)
return
}
// SendLike 发送赞
//
// 参数:
//
// - userId: 要赞的用户ID
//
// - times: 赞的次数
func (p *protocolAPI) SendLike(userId int64, times int) {
// 构建发送赞的JSON数据
var messageData wba.APIRequestInfo
messageData.Action = "send_like"
messageData.Params.UserId = userId
messageData.Params.Times = times
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("发送赞(SendLike)时,发送失败: %v", err)
return
}
LOG.Info("发送赞(SendLike)(至:%v):%v", userId, times)
return
}
// SetGroupKick 将指定用户移出群聊(需要群主或管理员权限)
//
// 参数:
//
// - groupId: 群号
//
// - userId: 用户ID
//
// - rejectAddRequest: 是否拒绝该用户的后续加群请求,可选值为 true 和 false
func (p *protocolAPI) SetGroupKick(groupId int64, userId int64, rejectAddRequest bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_kick"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.RejectAddRequest = rejectAddRequest
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("移出群聊(SetGroupKick)时,发送失败: %v", err)
return
}
LOG.Info("移出群聊(SetGroupKick)(从:%v-%v):%v", groupId, userId, rejectAddRequest)
return
}
// SetGroupBan 将指定用户禁言(需要群主或管理员权限)
//
// 参数:
//
// - groupId: 群号
//
// - userId: 用户ID
//
// - duration: 禁言时长单位为秒0表示取消禁言
func (p *protocolAPI) SetGroupBan(groupId int64, userId int64, duration int32) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_ban"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.Duration = duration
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("禁言群成员(SetGroupBan)时,执行失败: %v", err)
return
}
LOG.Info("禁言群成员(SetGroupBan)(在:%v-%v):%v", groupId, userId, duration)
return
}
// SetGroupWholeBan 设置全员禁言(需要群主或管理员权限)
//
// 参数:
//
// - groupId: 群号
//
// - enable: 是否启用全员禁言,可选值为 true 和 false
func (p *protocolAPI) SetGroupWholeBan(groupId int64, enable bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_whole_ban"
messageData.Params.GroupId = groupId
messageData.Params.Enable = enable
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("设置全员禁言(SetGroupWholeBan)时,执行失败: %v", err)
return
}
LOG.Info("设置全员禁言(SetGroupWholeBan)(在:%v):%v", groupId, enable)
return
}
// SetGroupAdmin 设置群管理员(需要群主权限)
//
// 参数:
//
// - groupId: 群号
//
// - userId: 用户ID
//
// - enable: 是否设置为管理员,可选值为 true 和 false
func (p *protocolAPI) SetGroupAdmin(groupId int64, userId int64, enable bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_admin"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.Enable = enable
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("设置群管理员(SetGroupAdmin)时,执行失败: %v", err)
return
}
LOG.Info("设置群管理员(SetGroupAdmin)(在:%v-%v):%v", groupId, userId, enable)
return
}
// SetGroupCard 设置群名片(可能需要群主或管理员权限)
//
// 参数:
//
// - groupId: 群号
//
// - userId: 用户ID
//
// - card: 新的群名片
func (p *protocolAPI) SetGroupCard(groupId int64, userId int64, card string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_card"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.Card = card
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("设置群名片(SetGroupCard)时,执行失败: %v", err)
return
}
LOG.Info("设置群名片(SetGroupCard)(在:%v-%v):%v", groupId, userId, card)
return
}
// SetGroupName 设置群名称(可能需要群主或管理员权限)
//
// 参数:
//
// - groupId: 群号
//
// - groupName: 新的群名称
func (p *protocolAPI) SetGroupName(groupId int64, groupName string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_name"
messageData.Params.GroupId = groupId
messageData.Params.GroupName = groupName
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("设置群名称(SetGroupName)时,执行失败: %v", err)
return
}
LOG.Info("设置群名称(SetGroupName)(在:%v):%v", groupId, groupName)
return
}
// SetGroupLeave 退出群聊
//
// 参数:
//
// - groupId: 群号
//
// - isDismiss: 是否解散群聊,仅当退出的是群主时有效,可选值为 true 和 false
func (p *protocolAPI) SetGroupLeave(groupId int64, isDismiss bool) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_leave"
messageData.Params.GroupId = groupId
messageData.Params.IsDismiss = isDismiss
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("退出群聊(SetGroupLeave)时,执行失败: %v", err)
return
}
LOG.Info("退出群聊(SetGroupLeave)(在:%v):%v", groupId, isDismiss)
return
}
// SetGroupSpecialTitle 设置群专属头衔(需要群主权限)
//
// 参数:
//
// - groupId: 群号
//
// - userId: 用户ID
//
// - specialTitle: 新的专属头衔
func (p *protocolAPI) SetGroupSpecialTitle(groupId int64, userId int64, specialTitle string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_special_title"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.SpecialTitle = specialTitle
messageData.Params.Duration = -1
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("设置群特殊头衔(SetGroupSpecialTitle)时,执行失败: %v", err)
return
}
LOG.Info("设置群特殊头衔(SetGroupSpecialTitle)(在:%v-%v):%v-%v", groupId, userId, specialTitle)
return
}
// SetFriendAddRequest 处理加好友请求
//
// 参数:
//
// - flag: 请求标识,由上报的事件中获得
//
// - approve: 是否同意请求,可选值为 true 和 false
//
// - remark: 设置好友的备注信息
func (p *protocolAPI) SetFriendAddRequest(flag string, approve bool, remark string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_friend_add_request"
messageData.Params.Flag = flag
messageData.Params.Approve = approve
messageData.Params.Remark = remark
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("处理加好友请求(SetFriendAddRequest)时,执行失败: %v", err)
return
}
LOG.Info("处理加好友请求(SetFriendAddRequest)(在:%v):%v-%v-%v", flag, approve, remark)
return
}
// SetGroupAddRequest 处理加群请求/邀请
//
// 参数:
//
// - flag: 请求标识,由上报的事件中获得
//
// - subType: 子类型,可能是 "invite" 或 "add", 由上报的事件中获得
//
// - approve: 是否同意请求,可选值为 true 和 false
//
// - reason: 拒绝请求的原因,仅当 approve 为 false 时有效
func (p *protocolAPI) SetGroupAddRequest(flag string, subType string, approve bool, reason string) {
var messageData wba.APIRequestInfo
messageData.Action = "set_group_add_request"
messageData.Params.Flag = flag
messageData.Params.SubType = subType
messageData.Params.Approve = approve
messageData.Params.Reason = reason
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("处理加群请求/邀请(SetGroupAddRequest)时,执行失败: %v", err)
return
}
LOG.Info("处理加群请求/邀请(SetGroupAddRequest)(在:%v-%v-%v):%v", flag, subType, approve, reason)
return
}
// SetRestart 重启
func (p *protocolAPI) SetRestart(delay int32) {
var messageData wba.APIRequestInfo
messageData.Action = "set_restart"
messageData.Params.Delay = delay
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("设置重启(SetRestart)时,执行失败: %v", err)
return
}
LOG.Info("设置重启(SetRestart):%v", delay)
return
}
// CleanCache 清理缓存
func (p *protocolAPI) CleanCache() {
var messageData wba.APIRequestInfo
messageData.Action = "clean_cache"
_, err := wsAPI(messageData)
if err != nil {
LOG.Error("清理缓存(CleanCache)时,执行失败: %v", err)
return
}
LOG.Info("清理缓存(CleanCache)")
return
}
// 2.有响应API需添加echo字段统一返回响应结构体
// GetLoginInfo 获取登录信息
func (p *protocolAPI) GetLoginInfo() (Response wba.APIResponseInfo) {
LOG.Info("获取登录信息(GetLoginInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_login_info"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取登录信息(GetLoginInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取登录信息(GetLoginInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetVersionInfo 获取协议信息
func (p *protocolAPI) GetVersionInfo() (Response wba.APIResponseInfo) {
LOG.Info("获取协议信息(GetVersionInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_version_info"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取协议信息(GetVersionInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取登录信息(GetVersionInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetMsg 获取消息
func (p *protocolAPI) GetMsg(messageId int32) (Response wba.APIResponseInfo) {
LOG.Info("获取消息(GetMsg)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_msg"
messageData.Params.MessageId = messageId
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取消息(GetMsg)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取消息(GetMsg)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetForwardMsg 获取合并转发消息
func (p *protocolAPI) GetForwardMsg(id string) (Response wba.APIResponseInfo) {
LOG.Info("获取合并转发消息(GetForwardMsg)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_forward_msg"
messageData.Params.Id = id
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取合并转发消息(GetForwardMsg)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取合并转发消息(GetForwardMsg)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetStrangerInfo 获取陌生人信息
func (p *protocolAPI) GetStrangerInfo(userId int64, noCache bool) (Response wba.APIResponseInfo) {
LOG.Info("获取陌生人信息(GetStrangerInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_stranger_info"
messageData.Params.UserId = userId
messageData.Params.NoCache = noCache
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取陌生人信息(GetStrangerInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取陌生人信息(GetStrangerInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetFriendList 获取好友列表
func (p *protocolAPI) GetFriendList() (Response wba.APIResponseInfo) {
LOG.Info("获取好友列表(GetFriendList)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_friend_list"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取好友列表(GetFriendList)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取好友列表(GetFriendList)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupList 获取群列表
func (p *protocolAPI) GetGroupList() (Response wba.APIResponseInfo) {
LOG.Info("获取群列表(GetGroupList)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_list"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取群列表(GetGroupList)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取群列表(GetGroupList)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupInfo 获取群信息
func (p *protocolAPI) GetGroupInfo(groupId int64, noCache bool) (Response wba.APIResponseInfo) {
LOG.Info("获取群信息(GetGroupInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_info"
messageData.Params.GroupId = groupId
messageData.Params.NoCache = noCache
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取群信息(GetGroupInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取群信息(GetGroupInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupMemberInfo 获取群成员信息
func (p *protocolAPI) GetGroupMemberInfo(groupId int64, userId int64, noCache bool) (Response wba.APIResponseInfo) {
LOG.Info("获取群成员信息(GetGroupMemberInfo)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_member_info"
messageData.Params.GroupId = groupId
messageData.Params.UserId = userId
messageData.Params.NoCache = noCache
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取群成员信息(GetGroupMemberInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取群成员信息(GetGroupMemberInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupMemberList 获取群成员列表
func (p *protocolAPI) GetGroupMemberList(groupId int64) (Response wba.APIResponseInfo) {
LOG.Info("获取群成员列表(GetGroupMemberList)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_group_member_list"
messageData.Params.GroupId = groupId
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取群成员列表(GetGroupMemberList)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取群成员列表(GetGroupMemberList)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetGroupHonorInfo 获取群荣誉信息
func (p *protocolAPI) 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.Type = Type
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取群荣誉信息(GetGroupHonorInfo)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取群荣誉信息(GetGroupHonorInfo)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetCookies 获取Cookies
func (p *protocolAPI) GetCookies(domain string) (Response wba.APIResponseInfo) {
LOG.Info("获取Cookies(GetCookies)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_cookies"
messageData.Params.Domain = domain
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取Cookies(GetCookies)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取Cookies(GetCookies)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetCSRFToken 获取CSRF Token
func (p *protocolAPI) GetCSRFToken() (Response wba.APIResponseInfo) {
LOG.Info("获取CSRF Token(GetCSRFToken)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_csrf_token"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取CSRF Token(GetCSRFToken)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取CSRF Token(GetCSRFToken)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetCredentials 获取登录令牌
func (p *protocolAPI) GetCredentials(domain string) (Response wba.APIResponseInfo) {
LOG.Info("获取登录令牌(GetCredentials)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_credentials"
messageData.Params.Domain = domain
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取登录令牌(GetCredentials)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取登录令牌(GetCredentials)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetRecord 获取语音
func (p *protocolAPI) GetRecord(file string, outFormat string) (Response wba.APIResponseInfo) {
LOG.Info("获取语音(GetRecord)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_record"
messageData.Params.File = file
messageData.Params.OutFormat = outFormat
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取语音(GetRecord)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取语音(GetRecord)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetImage 获取图片
func (p *protocolAPI) GetImage(file string) (Response wba.APIResponseInfo) {
LOG.Info("获取图片(GetImage)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_image"
messageData.Params.File = file
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取图片(GetImage)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取图片(GetImage)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// CanSendImage 检查是否可以发送图片
func (p *protocolAPI) CanSendImage() (Response wba.APIResponseInfo) {
LOG.Info("检查是否可以发送图片(CanSendImage)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "can_send_image"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("检查是否可以发送图片(CanSendImage)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("检查是否可以发送图片(CanSendImage)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// CanSendRecord 检查是否可以发送语音
func (p *protocolAPI) CanSendRecord() (Response wba.APIResponseInfo) {
LOG.Info("检查是否可以发送语音(CanSendRecord)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "can_send_record"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("检查是否可以发送语音(CanSendRecord)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("检查是否可以发送语音(CanSendRecord)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// GetStatus 获取状态
func (p *protocolAPI) GetStatus() (Response wba.APIResponseInfo) {
LOG.Info("获取状态(GetStatus)")
var messageData wba.APIRequestInfo
var err error
messageData.Action = "get_status"
messageData.Echo, err = GenerateUUID()
if err != nil {
LOG.Error("获取状态(GetStatus)时生成UUID失败: %v", err)
return wba.APIResponseInfo{}
}
Response, err = wsAPI(messageData)
if err != nil {
LOG.Error("获取状态(GetStatus)时,执行失败: %v", err)
return wba.APIResponseInfo{}
}
return Response
}
// 文件管理模块
//TODO: 文件管理模块待实现
//终端连接模块
//TODO: 终端模块待实现
//核心信息调用模块
var ProtocolApi protocolAPI
func GenerateUUID() (string, error) {
uuid := make([]byte, 16)
_, err := rand.Read(uuid)
if err != nil {
return "", err
}
// 设置UUID版本号版本4将第6字节的高4位设置为0100
uuid[6] = (uuid[6] & 0x0F) | 0x40
// 设置UUID变体RFC 4122规范定义的变体将第8字节的高4位设置为10
uuid[8] = (uuid[8] & 0x3F) | 0x80
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}

143
core/api_wst.go Normal file
View File

@ -0,0 +1,143 @@
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"encoding/json"
"strconv"
"strings"
)
type toolsAPI struct{}
//二、LOG模块
/*
关于LOG模块的说明
1.日志模块级别分为TRACEDEBUGINFONOTICEWARNERROR五个级别默认级别为INFO
2.日志模块提供LogWith方法可以自定义日志级别
3.日志模块提供Log方法默认日志级别为INFO
*/
// LogWith 打印日志(带级别)
//
// 参数:
// - level: 日志级别,支持"TRACE"、"DEBUG"、"INFO"、"NOTICE"、 "WARN"、"ERROR"
// - content: 日志内容
// - args: 可选参数,用于格式化日志内容
//
// 返回值:
// - 无
func (t *toolsAPI) LogWith(level string, content string, args ...interface{}) {
level = strings.ToLower(level)
switch level {
case "trace":
LOG.Trace(content, args...)
return
case "debug":
LOG.Debug(content, args...)
return
case "notice":
LOG.Notice(content, args...)
return
case "warn":
LOG.Warn(content, args...)
return
case "error":
LOG.Error(content, args...)
return
default:
LOG.Info(content, args...)
return
}
}
// Log 打印日志
//
// 参数:
// - content: 日志内容
// - args: 可选参数,用于格式化日志内容
func (t *toolsAPI) Log(content string, args ...interface{}) {
LOG.Info(content, args...)
}
// MsgUnmarshal 解析消息
//
// 参数:
// - messageJSON: 从数据库中获取的JSON序列化后的消息字符串
//
// 返回值:
// - wba.MessageEventInfo: 解析后的消息事件信息,解析失败时返回空结构体
func (t *toolsAPI) MsgUnmarshal(messageJSON string) (msg wba.MessageEventInfo) {
err := json.Unmarshal([]byte(messageJSON), &msg)
if err != nil {
return wba.MessageEventInfo{}
}
return msg
}
func (t *toolsAPI) SessionLabelAnalysis(sessionLabel wba.SessionLabel) wba.SessionInfo {
platform := strings.Split(sessionLabel, ":")[0]
sessionTypeAndId := strings.Split(sessionLabel, ":")[1]
sessionType := strings.Split(sessionTypeAndId, "-")[0]
sessionId := strings.Split(sessionTypeAndId, "-")[1]
Id, err := strconv.ParseInt(sessionId, 10, 64)
if err != nil {
return wba.SessionInfo{}
}
return wba.SessionInfo{
Platform: platform,
SessionType: sessionType,
SessionId: Id,
}
}
func (t *toolsAPI) VersionCompare(version1, version2 wba.VersionLabel) int {
version1Info := VersionLabelAnalysis(version1)
version2Info := VersionLabelAnalysis(version2)
if version1Info.BigVersion < version2Info.BigVersion {
return -1
} else if version1Info.BigVersion > version2Info.BigVersion {
return 1
} else {
if version1Info.SmallVersion < version2Info.SmallVersion {
return -1
} else if version1Info.SmallVersion > version2Info.SmallVersion {
return 1
} else {
if version1Info.FixVersion < version2Info.FixVersion {
return -1
} else if version1Info.FixVersion > version2Info.FixVersion {
return 1
} else {
return 0
}
}
}
}
func VersionLabelAnalysis(versionLabel wba.VersionLabel) wba.VersionInfo {
version := strings.Split(versionLabel, ".")
bigVersion, err := strconv.ParseUint(version[0], 10, 8)
if err != nil {
return wba.VersionInfo{}
}
smallVersion, err := strconv.ParseUint(version[1], 10, 8)
if err != nil {
return wba.VersionInfo{}
}
fixVersion, err := strconv.ParseUint(version[2], 10, 8)
if err != nil {
return wba.VersionInfo{}
}
return wba.VersionInfo{
BigVersion: uint8(bigVersion),
SmallVersion: uint8(smallVersion),
FixVersion: uint8(fixVersion),
}
}
var ToolsApi toolsAPI

286
core/app_admin.go Normal file
View File

@ -0,0 +1,286 @@
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"github.com/dop251/goja"
"os"
"path/filepath"
"reflect"
"strings"
)
type CamelCaseFieldNameMapper struct{}
func (CamelCaseFieldNameMapper) FieldName(_ reflect.Type, f reflect.StructField) string {
name := f.Name
if len(name) == 0 {
return name
}
// 首字母小写
return strings.ToLower(name[:1]) + name[1:]
}
func (CamelCaseFieldNameMapper) MethodName(_ reflect.Type, m reflect.Method) string {
name := m.Name
if len(name) == 0 {
return name
}
// 首字母小写
return strings.ToLower(name[:1]) + name[1:]
}
var GlobalCmdAgentSelector = wba.NewCmdAgentSelector()
var CmdMap = make(map[wba.AppKey]wba.CmdList)
var AppMap = make(map[wba.AppKey]wba.AppInfo)
var ScheduledTaskMap = make(map[wba.AppKey]map[string]wba.ScheduledTaskInfo)
// ReloadApps 重新加载应用
func ReloadApps() (total int, success int) {
// 清空AppMap和CmdMap
CmdMap = make(map[wba.AppKey]wba.CmdList)
AppMap = make(map[wba.AppKey]wba.AppInfo)
ScheduledTaskMap = make(map[wba.AppKey]map[string]wba.ScheduledTaskInfo)
GlobalCmdAgentSelector = wba.NewCmdAgentSelector()
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[AppCore.AppKey] = AppCore.CmdMap
GlobalCmdAgentSelector.AddCmdMap(CmdMap)
return total, success
}
// reloadAPP 重新加载单个应用
func reloadAPP(file os.DirEntry, appsDir string) (totalDelta int, successDelta int) {
if file.IsDir() {
return 0, 0
}
ext := filepath.Ext(file.Name())
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()
runtime.SetFieldNameMapper(CamelCaseFieldNameMapper{})
runtime.Set("console", map[string]interface{}{
"log": func(v ...interface{}) {
LOG.Info("JS log: %v", v...)
},
"error": func(v ...interface{}) {
LOG.Error("JS error: %v", v...)
},
})
// 添加错误捕获
safeRun := func(fn func() error) {
defer func() {
if r := recover(); r != nil {
LOG.Error("JS执行错误: %v", r)
}
}()
if err := fn(); err != nil {
LOG.Error("JS执行错误: %v", err)
}
}
// 修改JS代码执行部分
safeRun(func() error {
_, err := runtime.RunString(string(jsCode))
return err
})
// 创建JS可用的wbaObj对象
wbaObj := runtime.NewObject()
//wsp := runtime.NewObject()
//wsd := runtime.NewObject()
//wst := runtime.NewObject()
_ = runtime.Set("wba", wbaObj)
_ = wbaObj.Set("newApp", wba.NewApp)
_ = wbaObj.Set("withName", wba.WithSelector)
_ = wbaObj.Set("withDescription", wba.WithDescription)
_ = wbaObj.Set("withWebUrl", wba.WithWebUrl)
_ = wbaObj.Set("withLicense", wba.WithLicense)
_ = wbaObj.Set("wsp", ProtocolApi)
_ = wbaObj.Set("wsd", DatabaseApi)
_ = wbaObj.Set("wst", ToolsApi)
//_ = wbaObj.Set("wsp", wsp)
//_ = wbaObj.Set("wsd", wsd)
//_ = wbaObj.Set("wst", wst)
////WSP注册
//_ = wsp.Set("unsafelySendMsg", ProtocolApi.UnsafelySendMsg)
//_ = wsp.Set("unsafelySendPrivateMsg", ProtocolApi.UnsafelySendPrivateMsg)
//_ = wsp.Set("unsafelySendGroupMsg", ProtocolApi.UnsafelySendGroupMsg)
//_ = wsp.Set("sendMsg", ProtocolApi.SendMsg)
//_ = wsp.Set("sendPrivateMsg", ProtocolApi.SendPrivateMsg)
//_ = wsp.Set("sendGroupMsg", ProtocolApi.SendGroupMsg)
//_ = wsp.Set("unsafelyDeleteMsg", ProtocolApi.UnsafelyDeleteMsg)
//_ = wsp.Set("deleteMsg", ProtocolApi.DeleteMsg)
//_ = wsp.Set("sendLike", ProtocolApi.SendLike)
//_ = wsp.Set("setGroupKick", ProtocolApi.SetGroupKick)
//_ = wsp.Set("setGroupBan", ProtocolApi.SetGroupBan)
//_ = wsp.Set("setGroupWholeBan", ProtocolApi.SetGroupWholeBan)
//_ = wsp.Set("setGroupAdmin", ProtocolApi.SetGroupAdmin)
//_ = wsp.Set("setGroupLeave", ProtocolApi.SetGroupLeave)
//_ = wsp.Set("setGroupCard", ProtocolApi.SetGroupCard)
//_ = wsp.Set("setGroupName", ProtocolApi.SetGroupName)
//_ = wsp.Set("setGroupSpecialTitle", ProtocolApi.SetGroupSpecialTitle)
//_ = wsp.Set("setFriendAddRequest", ProtocolApi.SetFriendAddRequest)
//_ = wsp.Set("setGroupAddRequest", ProtocolApi.SetGroupAddRequest)
//_ = wsp.Set("getLoginInfo", ProtocolApi.GetLoginInfo)
//_ = wsp.Set("getVersionInfo", ProtocolApi.GetVersionInfo)
//_ = wsp.Set("getMsg", ProtocolApi.GetMsg)
//_ = wsp.Set("getGroupInfo", ProtocolApi.GetGroupInfo)
//_ = wsp.Set("getForwardMsg", ProtocolApi.GetForwardMsg)
//_ = wsp.Set("getStrangerInfo", ProtocolApi.GetStrangerInfo)
//_ = wsp.Set("getGroupList", ProtocolApi.GetGroupList)
//_ = wsp.Set("getGroupMemberList", ProtocolApi.GetGroupMemberList)
//_ = wsp.Set("getFriendList", ProtocolApi.GetFriendList)
//_ = wsp.Set("getGroupMemberInfo", ProtocolApi.GetGroupMemberInfo)
//_ = wsp.Set("getGroupHonorInfo", ProtocolApi.GetGroupHonorInfo)
//_ = wsp.Set("getStatus", ProtocolApi.GetStatus)
//_ = wsp.Set("getCookies", ProtocolApi.GetCookies)
//_ = wsp.Set("getCSRFToken", ProtocolApi.GetCSRFToken)
//_ = wsp.Set("getCredentials", ProtocolApi.GetCredentials)
//_ = wsp.Set("getImage", ProtocolApi.GetImage)
//_ = wsp.Set("getRecord", ProtocolApi.GetRecord)
//_ = wsp.Set("canSendImage", ProtocolApi.CanSendImage)
//_ = wsp.Set("canSendRecord", ProtocolApi.CanSendRecord)
//_ = wsp.Set("cetRestart", ProtocolApi.SetRestart)
//_ = wsp.Set("cleanCache", ProtocolApi.CleanCache)
//_ = wsp.Set("getVersionInfo", ProtocolApi.GetVersionInfo)
////WST注册
//_ = wst.Set("logWith", ToolsApi.LogWith)
//_ = wst.Set("log", ToolsApi.Log)
//_ = wst.Set("msgMarshal", ToolsApi.MsgUnmarshal)
////WSD注册
//_ = wsd.Set("setUserVariable", DatabaseApi.SetUserVariable)
//_ = wsd.Set("setGroupVariable", DatabaseApi.SetGroupVariable)
//_ = wsd.Set("setOutUserVariable", DatabaseApi.SetOutUserVariable)
//_ = wsd.Set("setOutGroupVariable", DatabaseApi.SetOutGroupVariable)
//_ = wsd.Set("unsafelySetUserVariable", DatabaseApi.UnsafelySetUserVariable)
//_ = wsd.Set("unsafelySetGroupVariable", DatabaseApi.UnsafelySetGroupVariable)
//_ = wsd.Set("unsafelySetGlobalVariable", DatabaseApi.UnsafelySetGlobalVariable)
//_ = wsd.Set("unsafelySetOutUserVariable", DatabaseApi.UnsafelySetOutUserVariable)
//_ = wsd.Set("unsafelySetOutGroupVariable", DatabaseApi.UnsafelySetOutGroupVariable)
//_ = wsd.Set("unsafelySetOutGlobalVariable", DatabaseApi.UnsafelySetOutGlobalVariable)
//_ = wsd.Set("getUserVariable", DatabaseApi.GetUserVariable)
//_ = wsd.Set("getGroupVariable", DatabaseApi.GetGroupVariable)
//_ = wsd.Set("getOutUserVariable", DatabaseApi.GetOutUserVariable)
//_ = wsd.Set("getOutGroupVariable", DatabaseApi.GetOutGroupVariable)
//_ = wsd.Set("unsafelyGetUserVariable", DatabaseApi.UnsafelyGetUserVariable)
//_ = wsd.Set("unsafelyGetGroupVariable", DatabaseApi.UnsafelyGetGroupVariable)
//_ = wsd.Set("unsafelyGetGlobalVariable", DatabaseApi.UnsafelyGetGlobalVariable)
//_ = wsd.Set("unsafelyGetOutUserVariable", DatabaseApi.UnsafelyGetOutUserVariable)
//_ = wsd.Set("unsafelyGetOutGroupVariable", DatabaseApi.UnsafelyGetOutGroupVariable)
//_ = wsd.Set("unsafelyGetOutGlobalVariable", DatabaseApi.UnsafelyGetOutGlobalVariable)
//_ = 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)
//_ = wsd.Set("unsafelyCreatePublicDatamap", DatabaseApi.UnsafelyCreatePublicDatamap)
// 获取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方法 %#v", pluginPath, initFunc)
// 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(jsApp, &appInfo); err != nil {
LOG.Error("应用信息转换失败: %v", err)
return 1, 0
}
// 初始化map字段
if appInfo.CmdMap == nil {
appInfo.CmdMap = make(map[string]wba.Cmd)
}
if appInfo.ScheduledTasks == nil {
appInfo.ScheduledTasks = make(map[string]wba.ScheduledTaskInfo)
}
AppMap[appInfo.AppKey] = appInfo
CmdMap[appInfo.AppKey] = appInfo.CmdMap
ScheduledTaskMap[appInfo.AppKey] = appInfo.ScheduledTasks
// 注册定时任务
for _, task := range appInfo.ScheduledTasks {
taskCopy := task
RegisterCron(appInfo.AppKey.Name, wba.ScheduledTaskInfo{
Name: taskCopy.Name,
Desc: taskCopy.Desc,
Cron: taskCopy.Cron,
Task: func() {
safeRun(func() error {
taskCopy.Task()
return nil
})
},
})
}
LOG.Info("JS应用 %s 加载成功", pluginPath)
return 1, 1
}
return 0, 0
}

View File

@ -1,78 +0,0 @@
//go:build linux || darwin
// +build linux darwin
package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"os"
"path/filepath"
"plugin"
)
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 == ".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)
LOG.INFO("应用 %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

@ -1,92 +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
}
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

@ -3,31 +3,27 @@ package core
import (
"ProjectWIND/LOG"
"ProjectWIND/wba"
"errors"
)
type CmdListInfo map[string]wba.Cmd
type AppInfo struct {
CmdMap map[string]wba.Cmd
AppKey wba.AppKey
}
func (app AppInfo) Get() AppInfo {
return app
}
func (app *AppInfo) Run(cmd string, args []string, msg wba.MessageEventInfo) error {
_, ok := app.CmdMap[cmd]
if !ok {
return errors.New("cmd not found")
}
app.CmdMap[cmd].SOLVE(args, msg)
return nil
}
func (app *AppInfo) Init(Api wba.WindAPI) error {
return nil
}
//func (app AppInfo) Get() AppInfo {
// return app
//}
//
//func (app *AppInfo) Run(cmd string, args []string, msg wba.MessageEventInfo) error {
// _, ok := app.CmdMap[cmd]
// if !ok {
// return errors.New("cmd not found")
// }
// app.CmdMap[cmd].Solve(args, msg)
// return nil
//}
func (app *AppInfo) GetCmd() map[string]wba.Cmd {
return app.CmdMap
@ -35,20 +31,35 @@ func (app *AppInfo) GetCmd() map[string]wba.Cmd {
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,
}
}
var AppCore = AppInfo{
AppKey: wba.AppKey{
Name: "core",
Level: 0,
Version: "1.0.0",
Selector: "core",
Option: "core",
},
CmdMap: CmdListInfo{
"bot": NewCmd(
"bot",
"显示WIND版本信息",
func(args []string, msg wba.MessageEventInfo) {
AppApi.SendMsg(msg, "WIND 0.1.0", false)
LOG.INFO("发送核心版本信息:(至:%v-%v:%v-%v)", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname)
ProtocolApi.SendMsg(msg, "WIND 0.1.0", false)
LOG.Info("发送核心版本信息:(至:%v-%v:%v-%v)", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname)
},
),
"help": NewCmd(
"help",
"显示帮助信息",
func(args []string, msg wba.MessageEventInfo) {
ProtocolApi.SendMsg(msg, "帮助信息", false)
LOG.Info("发送帮助信息:(至:%v-%v:%v-%v)", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname)
},
),
},

View File

@ -6,13 +6,13 @@ import (
"github.com/robfig/cron/v3"
)
func RegisterCron(task wba.ScheduledTaskInfo) {
func RegisterCron(appNames string, task wba.ScheduledTaskInfo) {
// 注册定时任务
c := cron.New(cron.WithSeconds())
_, err := c.AddFunc(task.Cron, task.Task)
if err != nil {
LOG.ERROR("添加定时任务 %s 时出错%v:", task.Name, err)
LOG.Error("添加定时任务 [%s]%s 时出错%v:", appNames, task.Name, err)
}
c.Start()
LOG.INFO("定时任务 %s 注册成功", task.Name)
LOG.Info("定时任务 [%s]%s 注册成功", appNames, task.Name)
}

View File

@ -2,9 +2,11 @@ package core
import (
"ProjectWIND/LOG"
"ProjectWIND/database"
"ProjectWIND/wba"
"encoding/json"
"fmt"
"strconv"
"strings"
)
@ -12,25 +14,25 @@ func HandleMessage(msgJson []byte) {
var msg wba.MessageEventInfo
err := json.Unmarshal(msgJson, &msg)
if err != nil {
LOG.ERROR("消息事件反序列化失败: %v", err)
LOG.Error("消息事件反序列化失败: %v", err)
}
// 处理消息
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)
LOG.Info("收到消息:(来自:%v-%v:%v-%v)%v", msg.MessageType, msg.GroupId, msg.UserId, msg.Sender.Nickname, msg.RawMessage)
isCmd, ok, err := CmdHandle(msg)
if err != nil {
LOG.Error("命令处理失败: %v", err)
}
if !isCmd && ok {
// 处理消息
// TODO: 处理消息
}
// TODO: 处理消息内容
}
func HandleNotice(msgJson []byte) {
var notice wba.NoticeEventInfo
err := json.Unmarshal(msgJson, &notice)
if err != nil {
LOG.ERROR("通知事件反序列化失败: %v", err)
LOG.Error("通知事件反序列化失败: %v", err)
}
// TODO: 处理通知
}
@ -39,7 +41,7 @@ func HandleRequest(msgJson []byte) {
var request wba.NoticeEventInfo
err := json.Unmarshal(msgJson, &request)
if err != nil {
LOG.ERROR("请求事件反序列化失败: %v", err)
LOG.Error("请求事件反序列化失败: %v", err)
}
// TODO: 处理请求
}
@ -48,39 +50,65 @@ func HandleMetaEvent(msgJson []byte) {
var meta wba.NoticeEventInfo
err := json.Unmarshal(msgJson, &meta)
if err != nil {
LOG.ERROR("元事件反序列化失败: %v", err)
LOG.Error("元事件反序列化失败: %v", err)
}
// TODO: 处理元事件
}
func CmdSplit(msg wba.MessageEventInfo) (string, []string) {
text := msg.RawMessage
if strings.HasPrefix(text, fmt.Sprintf("[CQ:at,qq=%d]", msg.SelfId)) {
text = strings.TrimPrefix(text, fmt.Sprintf("[CQ:at,qq=%d]", msg.SelfId))
} else {
if statusCheck(msg) {
return "", []string{}
func CmdHandle(msg wba.MessageEventInfo) (isCmd bool, ok bool, err error) {
// 获取消息的原始文本
text := msg.GetText()
// 初始化会话工作状态
session := wba.SessionInfo{}
session = session.Load("QQ", msg)
// 从数据库加载配置
conf, ok := database.MasterGet("GCAS_Config", session.SessionType, strconv.FormatInt(session.SessionId, 10), "GCAS_Config")
if !ok {
conf = "{}"
}
if err = GlobalCmdAgentSelector.LoadConfig(session, conf.(string)); err != nil {
LOG.Error("加载配置失败: %v", err)
return false, false, err
}
//检查有无application.CmdList中的命令前缀
// 检查命令前缀
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, " ")
cmdSlice := strings.Split(text, " ")
if text == "" {
return false, true, nil
}
cmd := cmdSlice[0]
args := cmdSlice[1:]
result, count := GlobalCmdAgentSelector.FindCmd(cmd, true)
if count == 0 {
LOG.Debug("未找到命令: %s", cmd)
return false, true, nil
}
// 执行找到的第一个匹配命令
cmdFunc := result[0]
if err := safeExecuteCmd(cmdFunc, args, msg); err != nil {
LOG.Error("执行命令失败: %v", err)
return true, false, err
}
return true, true, nil
}
}
}
}
return "", []string{}
return false, true, nil
}
func statusCheck(msg wba.MessageEventInfo) bool {
//TODO: 检查当前组群工作状态
return false
func safeExecuteCmd(cmdFunc wba.Cmd, args []string, msg wba.MessageEventInfo) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%v", r)
}
}()
cmdFunc.Solve(args, msg)
return nil
}
var cmdPrefix = []string{"/", "!", "", "", ".", "。"}

48
core/selector_doc.md Normal file
View File

@ -0,0 +1,48 @@
# Selector设计思路与使用说明
## 问题背景
在骰子执行指令时,我们经常遇到这样一些问题:
- 不同的插件可能使用相同的指令(很多跑团对应规则都有同名的指令),但是参数和功能完全不同,导致指令无法正常执行。并且当其中有系统级指令时,可能会导致其他严重问题,此时需要一个机制来区分指令。
- 当我们在进行trpg时日志开启后可能需要屏蔽某些无关的指令和自定义回复此时需要一个机制来过滤指令和回复即权限控制
- 在不同的群聊中,各种插件的开启情况各不相同,此时需要一个机制来管理不同组群插件的开启和关闭情况(包括前两条中的权限控制问题)。
为解决上述问题,我们设计了一个指令选择器,用于管理和选择指令。
## 设计思路
选择器的设计思路如下:
- 每个组群有一个独立的总选择器(Agent Selector),用于管理该组群的指令和回复。
- 每个选择器有若干个层级(Level),在查找可用指令(Cmd)时,从最高层级(0)开始查找,直到找到可用指令为止。
- 每个层级有若干个选择器(Selector),每个选择器中包含若干个选项(Option),每个选项对应一个指令集(CmdSet)。每个选择器只能同时选择一个选项,选择后其余选项中的指令集将被屏蔽。
- 指令集是由若干个应用(App)中的指令组成的,一个应用只能归属于一个指令集,一个指令集可以包含多个应用的指令。
数据结构如下:
```go
type AgentSelector struct {
Session wba.Session
Selectors []Selector
}
type Selector struct {
Level int
Name string
Options []*Option
}
type Option struct {
Name string
CmdSets map[string]CmdSet
}
type CmdSet struct {
map[string]wba.Command
}
type Cmmand struct {
Name string
Desc string
App AppKey
Solve func(*wba.Context)
}
```

45
core/web_service.go Normal file
View File

@ -0,0 +1,45 @@
package core
import (
"ProjectWIND/LOG"
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func WebServer(port string) *server.Hertz {
// 设置自定义日志记录器
hlog.SetLevel(hlog.LevelFatal)
h := server.Default(server.WithHostPorts("0.0.0.0:" + port))
LOG.Info("WebUI已启动监听端口%v", port)
h.Use(LoggingMiddleware())
h.GET("/index", func(ctx context.Context, c *app.RequestContext) {
//返回webui/index.html
c.File("./webui/index.html")
})
h.GET("/app/api/:appName/*action", func(ctx context.Context, c *app.RequestContext) {
appName := c.Param("appName")
action := c.Param("action")
message := appName + "-" + action + "\n"
c.String(consts.StatusOK, message)
})
return h
}
// LoggingMiddleware 是一个中间件函数,用于记录请求信息
func LoggingMiddleware() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
// 获取客户端 IP
clientIP := ctx.ClientIP()
// 获取请求完整路径
fullPath := ctx.Request.URI().PathOriginal()
// 继续处理请求
ctx.Next(c)
// 获取请求处理状态码
statusCode := ctx.Response.StatusCode()
// 在请求处理后记录结束信息
LOG.Debug("收到网络请求 | IP: %s | Path: %s | Status: %d ", clientIP, fullPath, statusCode)
}
}

View File

@ -2,6 +2,7 @@ package core
import (
"ProjectWIND/LOG"
"ProjectWIND/typed"
"ProjectWIND/wba"
"encoding/json"
"fmt"
@ -10,45 +11,43 @@ 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)
LOG.Error("Parse URL error: %v", err)
return err
}
// 创建一个带有Authorization头的HTTP请求
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
LOG.FATAL("创建请求出错:%v", err)
LOG.Fatal("创建请求出错:%v", err)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Authorization", "Bearer "+protocol.Token)
// 配置WebSocket连接升级器
dialer := websocket.DefaultDialer
// 使用升级器建立WebSocket连接
conn, _, err := dialer.Dial(req.URL.String(), req.Header)
if err != nil {
LOG.FATAL("建立WebSocket连接出错:%v", err)
LOG.Fatal("建立WebSocket连接出错:%v", err)
}
defer func(conn *websocket.Conn) {
err := conn.Close()
if err != nil {
LOG.ERROR("Close error: %v", err)
LOG.Error("Close error: %v", err)
}
}(conn)
LOG.INFO("已连接到WebSocket服务器: %v", u.String())
ProtocolInfo := AppApi.GetVersionInfo()
LOG.INFO("协议端信息: %v-%v", ProtocolInfo.Data.AppName, ProtocolInfo.Data.AppVersion)
logInfo := AppApi.GetLoginInfo()
LOG.INFO("连接到账号: %v%v", logInfo.Data.Nickname, logInfo.Data.UserId)
LOG.Info("已连接到WebSocket服务器: %v", u.String())
ProtocolInfo := ProtocolApi.GetVersionInfo()
LOG.Info("协议端信息: %v-%v", ProtocolInfo.Data.AppName, ProtocolInfo.Data.AppVersion)
logInfo := ProtocolApi.GetLoginInfo()
LOG.Info("连接到账号: %v%v", logInfo.Data.Nickname, logInfo.Data.UserId)
// 定义通道,缓存消息和消息类型,防止消息处理阻塞
messageChan := make(chan []byte, 32)
@ -57,7 +56,7 @@ func WebSocketHandler(protocolAddr string, token string) error {
// 接收消息并放入通道
messageType, message, err := conn.ReadMessage()
if err != nil {
LOG.ERROR("ReadMessage error: %v", err)
LOG.Error("ReadMessage error: %v", err)
return err
}
messageChan <- message
@ -78,14 +77,14 @@ func WebSocketHandler(protocolAddr string, token string) error {
// processMessage 处理接收到的消息
func processMessage(messageType int, message []byte) {
if messageType != websocket.TextMessage {
LOG.ERROR("Invalid message type: %v", messageType)
LOG.Error("Invalid message type: %v", messageType)
return
}
//message json解析
var messageMap map[string]interface{}
err := json.Unmarshal(message, &messageMap)
if err != nil {
LOG.ERROR("Unmarshal error: %v", err)
LOG.Error("Unmarshal error: %v", err)
return
}
// 处理接收到的消息
@ -126,28 +125,28 @@ 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)
LOG.Error("Parse URL error: %v", err)
return wba.APIResponseInfo{}, err
}
// 创建一个带有Authorization头的HTTP请求
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
LOG.FATAL("创建请求出错:%v", err)
LOG.Fatal("创建请求出错:%v", err)
}
req.Header.Set("Authorization", "Bearer "+gToken)
req.Header.Set("Authorization", "Bearer "+gProtocol.Token)
// 配置WebSocket连接升级器
dialer := websocket.DefaultDialer
// 使用升级器建立WebSocket连接
conn, _, err := dialer.Dial(req.URL.String(), req.Header)
if err != nil {
LOG.FATAL("建立WebSocket连接出错:%v", err)
LOG.Fatal("建立WebSocket连接出错:%v", err)
}
defer func(conn *websocket.Conn) {
err := conn.Close()
if err != nil {
LOG.ERROR("Close error: %v", err)
LOG.Error("Close error: %v", err)
}
}(conn)
err = conn.WriteMessage(websocket.TextMessage, bodyBytes)

View File

@ -11,6 +11,9 @@ import (
"time"
)
const address = "./data/database/datamaps.wdb"
const core = "./data/core.json"
type unit struct {
Id string
Data map[string]string
@ -20,29 +23,67 @@ type User unit
type Group unit
type Global unit
type Database struct {
type Configs struct {
Number map[string]int64
String map[string]string
Float map[string]float64
Number_Slice map[string][]int64
String_Slice map[string][]string
Hash string
}
type Datamap struct {
Id string
Permission string
Users map[string]User
Groups map[string]Group
Global map[string]Global
Configs Configs
}
func newDatabase(id string) Database {
// 创建数据库
db := &Database{
type Database struct {
Datamaps map[string]Datamap
}
func newDatamap(id string) Datamap {
// 创建数据表
db := &Datamap{
Id: id,
Permission: "private",
Users: make(map[string]User),
Groups: make(map[string]Group),
Global: make(map[string]Global),
Configs: Configs{
Number: make(map[string]int64),
String: make(map[string]string),
Float: make(map[string]float64),
Number_Slice: make(map[string][]int64),
String_Slice: make(map[string][]string),
Hash: "",
},
}
return *db
}
func newDatabase() Database {
// 创建数据库
db := &Database{
Datamaps: make(map[string]Datamap),
}
return *db
}
func (this *Database) addDatamap(id string) {
// 创建新数据表
db := newDatamap(id)
this.Datamaps[id] = 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 occured while create folder: %v", err)
LOG.Fatal("创建文件夹时出错: %v", err)
}
}
}
@ -54,12 +95,12 @@ func fileCheck(filename string) {
if _, err := os.Stat(filename); os.IsNotExist(err) {
file, err := os.Create(filename)
if err != nil {
LOG.FATAL("[ERROR]Error occured while create file: %v", err)
LOG.Fatal("创建文件时出错: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("[ERROR]Error occured while close file: %v", err)
LOG.Fatal("创建文件时出错: %v", err)
}
}(file)
}
@ -68,13 +109,13 @@ func fileCheck(filename string) {
func writeContent(f *os.File, str string) error {
// 写入内容到文件
if f == nil {
// log.Printf("[ERROR]file is nil")
LOG.ERROR("[ERROR]file is nil")
// log.Printf("[Error]file is nil")
LOG.Error("文件不存在")
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)
LOG.Error("无法写入到文件: %v", err)
return err
}
return nil
@ -90,17 +131,40 @@ func printContent(file string) (string, error) {
}
}
func getCorePassword() string {
// 获取核心密码
filename := core
fileCheck(filename)
dataJson, err := printContent(filename)
if err != nil {
LOG.Error("读取文件时出错 %s: %v", filename, err)
return ""
}
config := make(map[string]string)
err = json.Unmarshal([]byte(dataJson), config)
if err != nil {
LOG.Error("反序列化时出错: %v", err)
return ""
}
password, ok := config["password"]
if !ok {
LOG.Warn("core.json中未找到配置密码项")
return ""
}
return password
}
func saveData(db *Database) error {
// 保存数据到文件
dataJson, err := json.Marshal(db)
if err != nil {
LOG.ERROR("[ERROR]:Error while marshal data: %v", err)
LOG.Error("序列化数据时出错: %v", err)
return err
}
filename := "./database/" + db.Id + ".wdb"
filename := address
file, err := os.Create(filename)
if err != nil {
LOG.ERROR("[ERROR]:Error while create file %s: %v", filename, err)
LOG.Error("创建文件时出错 %s: %v", filename, err)
return err
}
writeContent(file, string(dataJson))
@ -109,18 +173,16 @@ func saveData(db *Database) error {
func loadData(db *Database) error {
// 读取配置文件
filename := "./database/" + db.Id + ".wdb"
filename := address
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)
LOG.Error("读文件时出错 %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)
LOG.Warn("反序列化数据时出错: %v", err)
return err
}
return nil
@ -128,116 +190,247 @@ func loadData(db *Database) error {
var DB *Database
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 data of %s is nil", id)
return "", false
}
value, ok := global.Data[key]
if !ok {
LOG.WARN("[WARNING]:Global data of %s's %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) {
func dataSet(datamap string, unit string, id string, key string, value interface{}, isAllowed bool, isMaster bool) {
// 修改数据
switch category {
case "user":
user, ok := db.Users[id]
dm, ok := DB.Datamaps[datamap]
if !ok {
db.Users[id] = User{
// 创建新数据表
DB.addDatamap(datamap)
dm = DB.Datamaps[datamap]
}
if !isAllowed && !isMaster && dm.Permission != "private" {
LOG.Warn("访问权限不足")
return
}
if !isMaster && dm.Permission == "master" {
LOG.Warn("访问权限不足")
return
}
switch unit {
case "config":
switch id {
case "number":
valueInt64, ok := value.(int64) // 断言value为int64类型
if !ok {
LOG.Error("配置值无法被断言为int64类型")
return
}
dm.Configs.Number[key] = valueInt64 // 使用断言后的int64值
case "string":
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
LOG.Error("配置值无法被断言为string类型")
return
}
dm.Configs.String[key] = valueStr // 使用断言后的string值
case "float":
valueFloat64, ok := value.(float64) // 断言value为float64类型
if !ok {
LOG.Error("配置值无法被断言为float64类型")
return
}
dm.Configs.Float[key] = valueFloat64 // 使用断言后的float64值
case "number_slice":
valueInt64Slice, ok := value.([]int64) // 断言value为[]int64类型
if !ok {
LOG.Error("配置值无法被断言为[]int64类型")
return
}
dm.Configs.Number_Slice[key] = valueInt64Slice // 使用断言后的[]int64值
case "string_slice":
valueStrSlice, ok := value.([]string) // 断言value为[]string类型
if !ok {
LOG.Error("配置值无法被断言为[]string类型")
return
}
dm.Configs.String_Slice[key] = valueStrSlice // 使用断言后的[]string值
case "hash":
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
LOG.Error("配置值无法被断言为string类型")
return
}
dm.Configs.Hash = valueStr // 使用断言后的string值
default:
LOG.Error("不合法的配置项类型 %s", id)
}
case "user":
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
LOG.Error("变量值无法被断言为string类型")
return
}
user, ok := dm.Users[id]
if !ok {
dm.Users[id] = User{
Id: id,
Data: make(map[string]string),
}
user = db.Users[id]
user = dm.Users[id]
}
if user.Data == nil {
user.Data = make(map[string]string)
}
user.Data[key] = value
user.Data[key] = valueStr // 使用断言后的string值
case "group":
group, ok := db.Groups[id]
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
db.Groups[id] = Group{
LOG.Error("变量值无法被断言为string类型")
return
}
group, ok := dm.Groups[id]
if !ok {
dm.Groups[id] = Group{
Id: id,
Data: make(map[string]string),
}
group = db.Groups[id]
group = dm.Groups[id]
}
if group.Data == nil {
group.Data = make(map[string]string)
}
group.Data[key] = value
group.Data[key] = valueStr // 使用断言后的string值
case "global":
global, ok := db.Global[id]
valueStr, ok := value.(string) // 断言value为string类型
if !ok {
db.Global[id] = Global{
LOG.Error("变量值无法被断言为string类型")
return
}
global, ok := dm.Global[id]
if !ok {
dm.Global[id] = Global{
Id: id,
Data: make(map[string]string),
}
global = db.Global[id]
global = dm.Global[id]
}
if global.Data == nil {
global.Data = make(map[string]string)
}
global.Data[key] = value
global.Data[key] = valueStr // 使用断言后的string值
default:
LOG.ERROR("[ERROR]:Invalid category %s", category)
LOG.Error("不合法的数据单元 %s", unit)
}
}
func dataGet(datamap string, unit string, id string, key string, isAllowed bool, isMaster bool) (interface{}, bool) {
dm, ok := DB.Datamaps[datamap]
if !ok {
LOG.Warn("数据表不存在 %s", datamap)
return "", false
}
if !isAllowed && !isMaster && dm.Permission != "private" {
LOG.Warn("访问权限不足")
return "", false
}
if !isMaster && dm.Permission == "master" {
LOG.Warn("访问权限不足")
return "", false
}
switch unit {
case "config":
switch id {
case "number":
value, ok := dm.Configs.Number[key]
if !ok {
LOG.Warn("配置项不存在%s", key)
return 0, false
}
return value, true
case "string":
value, ok := dm.Configs.String[key]
if !ok {
LOG.Warn("配置项不存在%s", key)
return "", false
}
return value, true
case "float":
value, ok := dm.Configs.Float[key]
if !ok {
LOG.Warn("配置项不存在%s", key)
return 0.0, false
}
return value, true
case "number_slice":
value, ok := dm.Configs.Number_Slice[key]
if !ok {
LOG.Warn("配置项不存在%s", key)
return []int64{}, false
}
return value, true
case "string_slice":
value, ok := dm.Configs.String_Slice[key]
if !ok {
LOG.Warn("配置项不存在%s", key)
return []string{}, false
}
return value, true
case "hash":
return dm.Configs.Hash, true
default:
LOG.Error("不合法的配置项类型 %s", id)
return "", false
}
case "user":
user, ok := dm.Users[id]
if !ok {
LOG.Warn("用户 %s 不存在", id)
return "", false
}
if user.Data == nil {
LOG.Warn("用户 %s 的数据显示为nil", id)
return "", false
}
value, ok := user.Data[key]
if !ok {
LOG.Warn("用户 %s 的数据中键 %s 不存在", id, key)
return "", false
}
return value, true
case "group":
group, ok := dm.Groups[id]
if !ok {
LOG.Warn("群组 %s 的数据不存在", id)
return "", false
}
if group.Data == nil {
LOG.Warn("群组 %s 的数据显示为nil", id)
return "", false
}
value, ok := group.Data[key]
if !ok {
LOG.Warn("群组 %s 的数据中键 %s 不存在", id, key)
return "", false
}
return value, true
case "global":
global, ok := dm.Global[id]
if !ok {
LOG.Warn("全局变量 %s 的数据不存在", id)
return "", false
}
if global.Data == nil {
LOG.Warn("全局变量 %s 的数据显示为nil", id)
return "", false
}
value, ok := global.Data[key]
if !ok {
LOG.Warn("全局变量 %s 的数据中键 %s 不存在", id, key)
return "", false
}
return value, true
default:
LOG.Error("Invalid unit %s", unit)
return "", false
}
}
func initializeDatabase() *Database {
// 启动并检查程序
LOG.INFO("Starting database ...")
db := newDatabase("datamap")
LOG.Info("正在启动数据库")
db := newDatabase()
loadData(&db)
LOG.INFO("Database started successfully.")
LOG.Info("数据库启动完成")
return &db
}
@ -249,7 +442,7 @@ func Start() {
signal.Notify(dataChan, syscall.SIGINT, syscall.SIGTERM)
// 定义一个Ticker用于每1小时触发一次保存操作
saveTicker := time.NewTicker(600 * time.Second)
saveTicker := time.NewTicker(3600 * time.Second)
defer saveTicker.Stop()
// 启动一个goroutine等待信号和定时保存
@ -258,12 +451,12 @@ func Start() {
select {
case <-dataChan:
// 接收到信号,保存数据并退出程序
LOG.INFO("Received signal, saving data and exiting...")
LOG.Info("关闭中,正在保存数据")
saveData(DB)
os.Exit(0)
case <-saveTicker.C:
// 定时保存数据
LOG.INFO("Saving data automatically...")
LOG.Info("自动保存数据")
saveData(DB)
}
}
@ -272,12 +465,123 @@ func Start() {
select {} // 阻塞
}
func Get(category string, id string, key string) (string, bool) {
// 查询数据
return dataGet(DB, category, id, key)
func CreatePublicDatamap(appName string, id string) {
// 查询权限
hash := getCorePassword()
if hash == "" {
// 删除数据表哈希
dataSet(appName, "config", "hash", "", "", true, true)
}
datahash, ok := dataGet(appName, "config", "hash", "", true, true)
if !ok {
// LOG.Error("[Error]:Error while get hash of %s", appName)
LOG.Error("获取应用数据表 %s 的密钥时出错", appName)
return
}
if hash != datahash {
LOG.Warn("应用 %s 没有创建公开数据表的权限", appName)
return
}
// 创建公开数据表
db, ok := DB.Datamaps[id]
if !ok {
db = newDatamap(id)
db.Permission = "public"
DB.Datamaps[id] = db
} else {
LOG.Info("数据表 %s 已经存在", id)
}
}
func Set(category string, id string, key string, value string) {
// 修改数据
dataSet(DB, category, id, key, value)
func MasterCreatePublicDatamap(id string) {
// 创建核心数据表
db, ok := DB.Datamaps[id]
if !ok {
db = newDatamap(id)
db.Permission = "master"
DB.Datamaps[id] = db
} else {
LOG.Info("数据表 %s 已经存在", id)
}
}
func MasterCreateMasterDatamap(id string) {
// 创建公开数据表
db, ok := DB.Datamaps[id]
if !ok {
db = newDatamap(id)
db.Permission = "public"
DB.Datamaps[id] = db
} else {
LOG.Info("数据表 %s 已经存在", id)
}
}
// 修改数据(核心)
func MasterSet(datamap string, unit string, id string, key string, value interface{}) {
dataSet(datamap, unit, id, key, value, true, true)
}
// 查询数据(核心)
func MasterGet(datamap string, unit string, id string, key string) (interface{}, bool) {
return dataGet(datamap, unit, id, key, true, true)
}
func Get(appName string, datamap string, unit string, id string, key string, isGettingConfig bool) (interface{}, bool) {
// 查询数据
if unit == "config" && id == "hash" {
// app不允许访问hash数据
LOG.Error("应用 %s 不允许访问数据库密钥", appName)
return "", false
}
if !isGettingConfig && unit == "config" {
// 不允许在非config数据表中访问config数据
LOG.Error("应用 %s 不能在常规读写中访问配置项信息,请使用配置项读取功能", appName)
return "", false
}
if appName != datamap {
// 需要master密码来访问其他app的数据
hash := getCorePassword()
if hash == "" {
// 删除数据表哈希
dataSet(appName, "config", "hash", "", "", true, true)
}
datahash, ok := dataGet(appName, "config", "hash", "", true, true)
if !ok {
LOG.Error("获取应用数据表 %s 的密钥时出错", appName)
}
if hash != datahash {
LOG.Warn("应用 %s 未被允许获取数据表 %s 的信息", appName, datamap)
return dataGet(appName, unit, id, key, false, false)
}
}
return dataGet(appName, unit, id, key, true, true)
}
func Set(appName string, datamap string, unit string, id string, key string, value interface{}) {
// 修改数据
if unit == "config" {
// app不允许修改config数据
LOG.Error("应用 %s 不允许修改配置项信息", appName)
return
}
if appName != datamap {
// 需要master密码来访问其他app的数据
hash := getCorePassword()
if hash == "" {
// 删除数据表哈希
dataSet(appName, "config", "hash", "", "", true, true)
}
datahash, ok := dataGet(appName, "config", "hash", "", true, true)
if !ok {
LOG.Error("获取应用数据表 %s 的密钥时出错", appName)
}
if hash != datahash {
LOG.Warn("应用 %s 未被允许修改数据表 %s 的信息", appName, datamap)
dataSet(appName, unit, id, key, value, false, false)
}
}
dataSet(appName, unit, id, key, value, true, false)
}

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为要修改的变量值

28
go.mod
View File

@ -2,9 +2,33 @@ module ProjectWIND
go 1.23.2
require github.com/gorilla/websocket v1.5.3
require (
github.com/cloudwego/hertz v0.9.5
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17
github.com/gorilla/websocket v1.5.3
github.com/robfig/cron/v3 v3.0.1
)
require (
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/bytedance/gopkg v0.1.0 // indirect
github.com/bytedance/sonic v1.12.0 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cloudwego/netpoll v0.6.4 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/nyaruka/phonenumbers v1.0.55 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.3.8 // indirect
google.golang.org/protobuf v1.27.1 // indirect
)

93
go.sum
View File

@ -1,6 +1,99 @@
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/bytedance/gopkg v0.1.0 h1:aAxB7mm1qms4Wz4sp8e1AtKDOeFLtdqvGiUe7aonRJs=
github.com/bytedance/gopkg v0.1.0/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ=
github.com/bytedance/mockey v1.2.12 h1:aeszOmGw8CPX8CRx1DZ/Glzb1yXvhjDh6jdFBNZjsU4=
github.com/bytedance/mockey v1.2.12/go.mod h1:3ZA4MQasmqC87Tw0w7Ygdy7eHIc2xgpZ8Pona5rsYIk=
github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls=
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/hertz v0.9.5 h1:FXV2YFLrNHRdpwT+OoIvv0wEHUC0Bo68CDPujr6VnWo=
github.com/cloudwego/hertz v0.9.5/go.mod h1:UUBt8N8hSTStz7NEvLZ5mnALpBSofNL4DoYzIIp8UaY=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cloudwego/netpoll v0.6.4 h1:z/dA4sOTUQof6zZIO4QNnLBXsDFFFEos9OOGloR6kno=
github.com/cloudwego/netpoll v0.6.4/go.mod h1:BtM+GjKTdwKoC8IOzD08/+8eEn2gYoiNLipFca6BVXQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17 h1:spJaibPy2sZNwo6Q0HjBVufq7hBUj5jNFOKRoogCBow=
github.com/dop251/goja v0.0.0-20250125213203-5ef83b82af17/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg=
github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

21
main.go
View File

@ -12,8 +12,14 @@ func main() {
//如果没有参数则启动WebUI
if len(os.Args) <= 1 {
initCore()
fmt.Println("请修改配置文件后,使用-p参数连接协议端开始运行。")
return
// 启动WebUI
h := core.WebServer("8080")
go h.Spin()
// 连接到数据库
go startDatabase()
// 连接到协议端
go startProtocol()
select {}
}
cmdArgs := os.Args[1:]
if cmdArgs[0] == "-h" || cmdArgs[0] == "--help" {
@ -42,17 +48,6 @@ func main() {
registerService()
return
}
if cmdArgs[0] == "-p" || cmdArgs[0] == "--protocol" {
// 连接到协议端
go AutoSave()
startProtocol()
return
}
if cmdArgs[0] == "-d" || cmdArgs[0] == "--database" {
// 连接到数据库
startDatabase()
return
}
fmt.Println("未知命令,请使用-h查看帮助。")
return
}

View File

@ -1,4 +1,4 @@
# ProjectWIND
# WIND
像风一样自由
<div style="text-align: center;"><img src="./logo.png" alt="ProjectWIND" width="50%" ></div>
@ -8,20 +8,38 @@ license: [MIT](./LICENSE)
🚧🚧🚧🚧施工中ing…… 🚧🚧🚧🚧
> warning: 该项目正在开发中,请勿用于生产环境
TODO:
- ✅ 协议端通信
- ✅ 日志输出
- ✅ 文件初始化
- ✅ 事件处理
- ✅ 数据库交互
- ❌ 插件系统
- ❌ 用户系统
- ❌ web ui
- ❌ 文档编写
## WIND 是什么?
WIND全称WIND is not dice是一个基于 Go 语言开发的bot框架旨在以高自由度完成各种功能。
WIND ( *WIND is not dice* ) 是一个基于 Go 语言开发的bot框架旨在以高自由度完成各种功能。
### 特点
- 基于 Golang 的核心,轻量高效,跨平台支持
- 基于onebot11协议可对接各种平台
- 利用goja实现的js插件系统高效灵活的插件实现
- 定时任务、API事件响应等多种触发方式满足各种需求
- 同时支持webUI和Terminal交互方便开发调试和维护管理
- 支持多用户系统灵活管理用户权限高效资源共享一个核心运行多个bot
---
TODO:
- [ ] 底层框架
- [x] 单协议通信
- [ ] 文件资源管理
- [ ] 多用户系统
- [ ] 数据库管理
- [ ] 日志输出
- [ ] 配置文件
- [ ] 插件管理
- [x] 基础事件处理
- [ ] 指令管理
- [ ] 定时任务管理
- [ ] API管理
- [ ] web ui
- [ ] 文档编写
- [ ] 手册编写
---
# 项目依赖与致谢
@ -30,14 +48,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,10 +1,28 @@
package typed
import "ProjectWIND/wba"
type CoreConfigInfo struct {
CoreName string `json:"core_name"`
ProtocolAddr string `json:"protocol_addr"`
Token string `json:"token"`
Protocol Protocol `json:"protocol"`
WebUIPort uint16 `json:"webui_port"`
PasswordHash string `json:"password_hash"`
ServiceName string `json:"service_name"`
}
type Protocol struct {
Name string `json:"protocol_name"`
Platform string `json:"protocol_platform"`
Addr string `json:"protocol_addr"`
Token string `json:"token"`
Enable bool `json:"enable"`
}
type SessionWorkSpace struct {
SessionId string `json:"session_id"`
Rule string `json:"rule"`
Enable bool `json:"enable"`
AppEnable map[wba.AppKey]bool `json:"app_enable"`
CmdEnable map[string]bool `json:"cmd_enable"`
WorkLevel int32 `json:"work_level"`
}

158
utils.go
View File

@ -19,11 +19,11 @@ func initCore() string {
log.SetFlags(log.Ldate | log.Ltime)
log.SetPrefix("[WIND] ")
LOG.INFO("正在初始化WIND配置文件...")
LOG.Info("正在初始化WIND配置文件...")
err := checkAndUpdateConfig("./data/core.json")
if err != nil {
LOG.FATAL("初始化时,加载配置文件 ./data/core.json 失败: %v", err)
LOG.Fatal("初始化时,加载配置文件 ./data/core.json 失败: %v", err)
}
// 创建日志文件
logFile := fmt.Sprintf("./data/log/WIND_CORE_%s.log", time.Now().Format("20060102150405"))
@ -31,30 +31,30 @@ func initCore() string {
if os.IsNotExist(err) {
file, err := os.Create(logFile)
if err != nil {
LOG.FATAL("初始化时,创建日志文件失败: %v", err)
LOG.Fatal("初始化时,创建日志文件失败: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭日志文件: %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("初始化时,无法打开日志文件: %v", err)
LOG.Fatal("初始化时,无法打开日志文件: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭日志文件: %v", err)
LOG.Fatal("无法关闭日志文件: %v", err)
}
}(file)
// 设置日志输出到文件
log.SetOutput(io.MultiWriter(os.Stdout, file))
LOG.INFO("WIND配置文件初始化完成")
LOG.Info("WIND配置文件初始化完成")
return logFile
}
@ -64,7 +64,7 @@ func checkAndUpdateConfig(configPath string) error {
// 如果不存在,则创建该文件夹
err := os.Mkdir("./data/", 0755)
if err != nil {
LOG.FATAL("初始化时创建data文件夹失败: %v", err)
LOG.Fatal("初始化时创建data文件夹失败: %v", err)
}
}
@ -73,12 +73,12 @@ func checkAndUpdateConfig(configPath string) error {
// 如果不存在,则创建该文件
file, err := os.Create("./data/core.json")
if err != nil {
LOG.FATAL("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
LOG.Fatal("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("关闭 ./data/core.json 配置文件失败: %v", err)
LOG.Fatal("关闭 ./data/core.json 配置文件失败: %v", err)
}
}(file)
}
@ -86,11 +86,17 @@ func checkAndUpdateConfig(configPath string) error {
// 检查并更新配置文件
var coreConfig typed.CoreConfigInfo
// 定义默认配置
var defaultProtocol typed.Protocol
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.ProtocolAddr = ""
defaultConfig.Protocol = defaultProtocol
defaultConfig.ServiceName = "wind"
// 读取配置文件
file, err := os.Open(configPath)
@ -100,7 +106,7 @@ func checkAndUpdateConfig(configPath string) error {
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
LOG.Fatal("无法关闭配置文件 ./data/core.json: %v", err)
}
}(file)
@ -114,8 +120,8 @@ func checkAndUpdateConfig(configPath string) error {
}
// 检查并更新配置
if coreConfig.ProtocolAddr == "" {
coreConfig.ProtocolAddr = defaultConfig.ProtocolAddr
if coreConfig.Protocol == (typed.Protocol{}) {
coreConfig.Protocol = defaultConfig.Protocol
}
if coreConfig.WebUIPort == 0 {
coreConfig.WebUIPort = defaultConfig.WebUIPort
@ -129,9 +135,9 @@ func checkAndUpdateConfig(configPath string) error {
if coreConfig.PasswordHash == "" {
coreConfig.PasswordHash = ""
}
if coreConfig.Token == "" {
coreConfig.Token = ""
}
//if coreConfig.Token == "" {
// coreConfig.Token = ""
//}
formattedJSON, err := json.MarshalIndent(coreConfig, "", " ")
if err != nil {
@ -141,19 +147,19 @@ func checkAndUpdateConfig(configPath string) error {
// 将格式化后的JSON字符串写入文件
file, err = os.Create("./data/core.json")
if err != nil {
LOG.FATAL("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
LOG.Fatal("初始化时,创建 ./data/core.json 配置文件失败: %v", err)
return err
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
LOG.Fatal("无法关闭配置文件 ./data/core.json: %v", err)
}
}(file)
_, err = file.Write(formattedJSON)
if err != nil {
LOG.FATAL("初始化时,写入 ./data/core.json 配置文件失败: %v", err)
LOG.Fatal("初始化时,写入 ./data/core.json 配置文件失败: %v", err)
return err
}
@ -170,42 +176,42 @@ func checkAndUpdateConfig(configPath string) error {
err = checkDataFolderExistence("./data/app/")
if err != nil {
LOG.FATAL("创建应用文件夹 ./data/app/ 失败: %v", err)
LOG.Fatal("创建应用文件夹 ./data/app/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/images/")
if err != nil {
LOG.FATAL("创建图片文件夹 ./data/images/ 失败: %v", err)
LOG.Fatal("创建图片文件夹 ./data/images/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/files/")
if err != nil {
LOG.FATAL("创建文件文件夹 ./data/files/ 失败: %v", err)
LOG.Fatal("创建文件文件夹 ./data/files/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/videos/")
if err != nil {
LOG.FATAL("创建视频文件夹 ./data/videos/ 失败: %v", err)
LOG.Fatal("创建视频文件夹 ./data/videos/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/audios/")
if err != nil {
LOG.FATAL("创建音频文件夹 ./data/audios/ 失败: %v", err)
LOG.Fatal("创建音频文件夹 ./data/audios/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/database/")
if err != nil {
LOG.FATAL("创建数据库文件夹 ./data/database/ 失败: %v", err)
LOG.Fatal("创建数据库文件夹 ./data/database/ 失败: %v", err)
return err
}
err = checkDataFolderExistence("./data/log/")
if err != nil {
LOG.FATAL("创建日志文件夹 ./data/log/ 失败: %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)
LOG.Fatal("创建应用配置文件夹 ./data/app/configs/ 失败: %v", err)
}
return nil
@ -221,18 +227,18 @@ func startWebUI() {
// 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
LOG.FATAL("打开日志文件失败: %v", err)
LOG.Fatal("打开日志文件失败: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭日志文件: %v", err)
LOG.Fatal("无法关闭日志文件: %v", err)
}
}(file)
// 设置日志输出到文件
log.SetOutput(io.MultiWriter(os.Stdout, file))
LOG.INFO("正在启动WIND核心服务...")
LOG.Info("正在启动WIND核心服务...")
// 启动 WebSocket 处理程序
//TODO: 这里要添加webUI的启动代码
@ -248,17 +254,18 @@ func registerService() {
// 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
LOG.FATAL("无法打开日志文件: %v", err)
LOG.Fatal("无法打开日志文件: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭日志文件: %v", err)
LOG.Fatal("无法关闭日志文件: %v", err)
}
}(file)
// 设置日志输出到文件
log.SetOutput(io.MultiWriter(os.Stdout, file))
//TODO: 这里要添加注册服务的代码
//在/etc/systemd/system/下创建服务文件
}
func startProtocol() {
@ -270,92 +277,71 @@ func startProtocol() {
// 打开日志文件
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
LOG.FATAL("无法打开日志文件: %v", err)
LOG.Fatal("无法打开日志文件: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭日志文件: %v", err)
LOG.Fatal("无法关闭日志文件: %v", err)
}
}(file)
// 设置日志输出到文件
log.SetOutput(io.MultiWriter(os.Stdout, file))
ReloadApps()
//从配置文件中读取配置信息
LOG.INFO("正在启动WIND协议服务...")
LOG.Info("正在启动WIND协议服务...")
var config typed.CoreConfigInfo
file, err = os.Open("./data/core.json")
if err != nil {
LOG.FATAL("无法打开配置文件 ./data/core.json: %v", err)
LOG.Fatal("无法打开配置文件 ./data/core.json: %v", err)
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
LOG.FATAL("无法关闭配置文件 ./data/core.json: %v", err)
LOG.Fatal("无法关闭配置文件 ./data/core.json: %v", err)
}
}(file)
decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
if err != nil {
LOG.FATAL("连接协议时,解析配置文件 ./data/core.json 失败: %v", err)
LOG.Fatal("连接协议时,解析配置文件 ./data/core.json 失败: %v", err)
}
LOG.Info("正在启动WebSocket链接程序...")
protocol := config.Protocol
if protocol.Name == "EXAMPLE" {
LOG.Warn("未找到协议配置信息")
return
}
if protocol.Name == "" {
LOG.Warn("连接协议 %s 时,协议名称为空", protocol.Name)
return
}
//获取协议地址
protocolAddr := config.ProtocolAddr
//获取token
token := config.Token
//链接协议
// 启动 WebSocket 处理程序
LOG.INFO("正在启动WebSocket链接程序...")
err = core.WebSocketHandler(protocolAddr, token)
if err != nil {
// 如果发生错误,记录错误并退出程序
LOG.FATAL("连接协议时,启动 WebSocket 处理程序失败: %v", err)
}
protocolAddr := protocol.Addr
if protocolAddr == "" {
LOG.Warn("连接协议 %s 时,协议地址为空", protocol.Name)
return
}
func AutoSave() {
for {
time.Sleep(time.Second * 60)
LOG.INFO("自动保存")
//TODO: 这里要添加自动保存的代码
}
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)
}
}
func ReloadApps() {
LOG.INFO("正在重新加载应用...")
LOG.Info("正在重新加载应用...")
total, success := core.ReloadApps()
LOG.INFO("应用重新加载完成,共加载%d个应用成功加载%d个应用。", total, success)
LOG.Info("应用重新加载完成,共加载%d个应用成功加载%d个应用。", total, success)
}
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 {}
}

47
wba/abandoned.go Normal file
View File

@ -0,0 +1,47 @@
package wba
//type APP interface {
// Get() AppInfo
// Init(Wba interface{}) error
// //Init(WspApi WindStandardProtocolAPI, WsdApi WindStandardDataBaseAPI, WstApi WindStandardTools) error
// //InitWSD(api WindStandardDataBaseAPI) error
//}
//func (ai AppInfo) Get() AppInfo {
// return ai
//}
//
//func (ai AppInfo) Init(Wba interface{}) error {
// WBA = Wba.(WindStandardTools)
// return nil
//}
//func (ai *AppInfo) Init(WspApi WindStandardProtocolAPI, WsdApi WindStandardDataBaseAPI, WstApi WindStandardTools) error {
// WSP = WspApi
// WSD = WsdApi
// WST = WstApi
// return nil
//}
//func (ai *AppInfo) InitWSD(api WindStandardDataBaseAPI) error {
// WSD = api
// return nil
//}
//func WithName(name string) AppInfoOption {
// return func(ei *AppInfo) {
// ei.AppKey.Name = name
// }
//}
//
//func WithVersion(version string) AppInfoOption {
// return func(ei *AppInfo) {
// ei.AppKey.Version = version
// }
//}
//
//func WithAuthor(author string) AppInfoOption {
// return func(ei *AppInfo) {
// ei.Author = author
// }
//}

103
wba/api_request_response.go Normal file
View File

@ -0,0 +1,103 @@
package wba
type APIRequestInfo struct {
Action string `json:"action,omitempty"`
Params ParamsInfo `json:"params"`
Echo string `json:"echo,omitempty"`
}
type APIResponseInfo struct {
Status string `json:"status,omitempty"`
Retcode int64 `json:"retcode,omitempty"`
Data ResponseDataInfo `json:"data,omitempty"`
Echo string `json:"echo,omitempty"`
}
type APIResponseListInfo struct {
Status string `json:"status,omitempty"`
Retcode int64 `json:"retcode,omitempty"`
Data []ResponseDataInfo `json:"data,omitempty"`
Echo string `json:"echo,omitempty"`
}
type ResponseDataInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Sex string `json:"sex,omitempty"`
Age int32 `json:"age,omitempty"`
Remark string `json:"remark,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
GroupName string `json:"group_name,omitempty"`
MemberCount int32 `json:"member_count,omitempty"`
MaxMemberCount int32 `json:"max_member_count,omitempty"`
Card string `json:"card,omitempty"`
Area string `json:"area,omitempty"`
JoinTime int32 `json:"join_time,omitempty"`
LastSentTime int32 `json:"last_sent_time,omitempty"`
Level string `json:"level,omitempty"`
Role string `json:"role,omitempty"`
Unfriendly bool `json:"unfriendly,omitempty"`
Title string `json:"title,omitempty"`
TitleExpireTime int32 `json:"title_expire_time,omitempty"`
CardChangeable bool `json:"card_changeable,omitempty"`
CurrentTalkative CurrentTalkativeInfo `json:"current_talkative,omitempty"`
TalkativeList []CurrentTalkativeInfo `json:"talkative_list,omitempty"`
PerformerList []HonorInfo `json:"performer_list,omitempty"`
LegendList []HonorInfo `json:"legend_list,omitempty"`
StrongNewbieList []HonorInfo `json:"strong_newbie_list,omitempty"`
EmoticonList []HonorInfo `json:"emoticon_list,omitempty"`
Cookies string `json:"cookies,omitempty"`
Token string `json:"token,omitempty"`
CsrfToken string `json:"csrf_token,omitempty"`
File string `json:"file,omitempty"`
OutFormat string `json:"out_format,omitempty"`
Yes bool `json:"yes,omitempty"`
Online bool `json:"online,omitempty"`
Good bool `json:"good,omitempty"`
AppName string `json:"app_name,omitempty"`
AppVersion string `json:"app_version,omitempty"`
ProtocolVersion string `json:"protocol_version,omitempty"`
Time int64 `json:"time,omitempty"`
MessageType string `json:"message_type,omitempty"`
MessageId int32 `json:"message_id,omitempty"`
RealId int32 `json:"real_id,omitempty"`
Sender SenderInfo `json:"sender,omitempty"`
Message []MessageDataInfo `json:"message,omitempty"`
}
type CurrentTalkativeInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Avatar string `json:"avatar,omitempty"`
DayCount int32 `json:"day_count,omitempty"`
}
type HonorInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Avatar string `json:"avatar,omitempty"`
Description string `json:"Description,omitempty"`
}
// SegmentInfo 消息段
type SegmentInfo struct {
Type string `json:"type,omitempty"`
Data SegmentDataInfo `json:"data,omitempty"`
}
type SegmentDataInfo struct {
Type string `json:"type,omitempty"`
QQ string `json:"qq,omitempty"`
Id int64 `json:"id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Content string `json:"content,omitempty"`
Url string `json:"url,omitempty"`
Lat string `json:"lat,omitempty"`
Lon string `json:"lon,omitempty"`
Title string `json:"title,omitempty"`
Audio string `json:"audio,omitempty"`
Image string `json:"image,omitempty"`
Video string `json:"video,omitempty"`
Data string `json:"data,omitempty"`
}

206
wba/app.go Normal file
View File

@ -0,0 +1,206 @@
package wba
import "strings"
type AppInfo struct {
AppKey AppKey
Author string
Description string
Homepage string
License 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 (ai *AppInfo) AddCmd(name string, cmd Cmd) {
ai.CmdMap[name] = cmd
}
func (ai *AppInfo) AddNoticeEventHandler(ScheduledTask ScheduledTaskInfo) {
ai.ScheduledTasks[ScheduledTask.Name] = ScheduledTask
}
func (ai *AppInfo) AddScheduledTask(task ScheduledTaskInfo) {
ai.ScheduledTasks[task.Name] = task
}
type AppInfoOption func(ei *AppInfo)
func WithDescription(description string) AppInfoOption {
return func(ei *AppInfo) {
ei.Description = description
}
}
func WithWebUrl(webUrl string) AppInfoOption {
return func(ei *AppInfo) {
ei.Homepage = webUrl
}
}
func WithLicense(license string) AppInfoOption {
return func(ei *AppInfo) {
ei.License = license
}
}
func WithSelector(level uint8, selector string, option string) AppInfoOption {
return func(ei *AppInfo) {
ei.AppKey.Level = level
ei.AppKey.Option = option
ei.AppKey.Selector = selector
}
}
func WithLevel(level uint8) AppInfoOption {
if level < 1 {
level = 1
}
return func(ei *AppInfo) {
ei.AppKey.Level = level
}
}
func toCamelCase(s string) string {
if s == "" {
return s
}
return strings.ToLower(s[:1]) + s[1:]
}
func NewApp(name string, version string, author string, opts ...AppInfoOption) AppInfo {
Ext := AppInfo{
AppKey: AppKey{
Name: name,
Version: version,
Level: 2,
Selector: name,
Option: name,
},
Author: author,
Description: "A simple and easy-to-use bot framework",
Homepage: "https://github.com/Sheyiyuan/wind_app_model",
License: "MIT",
CmdMap: make(map[string]Cmd),
ScheduledTasks: map[string]ScheduledTaskInfo{},
API: map[string]interface{}{},
}
for _, opt := range opts {
opt(&Ext)
}
// 添加JS风格方法
Ext.API = map[string]interface{}{
toCamelCase("NewCmd"): Ext.NewCmd,
toCamelCase("AddCmd"): Ext.AddCmd,
toCamelCase("NewScheduledTask"): Ext.NewScheduledTask,
toCamelCase("AddScheduledTask"): Ext.AddScheduledTask,
}
return Ext
}
func (ai *AppInfo) NewCmd(name string, description string, solve func(args []string, msg MessageEventInfo)) Cmd {
return Cmd{
Name: name,
Desc: description,
Solve: solve,
AppKey: ai.AppKey,
}
}
func (ai *AppInfo) 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
Solve func(args []string, msg MessageEventInfo)
AppKey AppKey
}
type ScheduledTaskInfo struct {
Name string `json:"Name,omitempty"`
Desc string `json:"desc,omitempty"`
Task func() `json:"task,omitempty"`
Cron string `json:"cron,omitempty"`
}
type AppKey struct {
Name string `json:"name"`
Level Priority `json:"level"`
Version string `json:"version"`
Selector SelectorLabel `json:"selector"`
Option OptionLabel `json:"option"`
}
// Priority 是一个整数类型用于表示命令的优先级。只能是不小于1且不大于4的整数。
type Priority = uint8
// VersionLabel 是一个字符串类型,用于表示版本标签。
type VersionLabel = string
// SelectorLabel 是一个字符串类型,用于表示选择器的标签。
type SelectorLabel = string
// OptionLabel 是一个字符串类型,用于表示选择器选项的标签。
type OptionLabel = string
// SessionLabel 是一个字符串类型,用于表示聊天会话的标签,格式为[平台:类型-ID],如"QQ:group-1145141919810"
type SessionLabel = string
// CmdSetLabel 是一个字符串类型,用于表示命令集的标签。
type CmdSetLabel = string
// CmdLabel 是一个字符串类型,用于表示命令的标签。
type CmdLabel = string
// CmdList 是一个字符串到 wba.Cmd 的映射,用于存储命令的列表。
type CmdList = map[string]Cmd
// SessionInfo 是一个结构体,用于存储会话信息。
//
// 字段:
// - Platform: 表示会话的平台,如"QQ"、"Telegram"等。
// - SessionType: 表示会话的类型,如"group"、"private"等。
// - SessionId: 表示会话的ID如群号、私聊号等。
type SessionInfo struct {
Platform string
SessionType string
SessionId int64
}
func (s *SessionInfo) Load(platform string, msg MessageEventInfo) SessionInfo {
s.Platform = platform
s.SessionType = msg.MessageType
if s.SessionType == "group" {
s.SessionId = msg.GroupId
}
if s.SessionType == "private" {
s.SessionId = msg.UserId
}
return *s
}
// VersionInfo 是一个结构体,用于存储版本信息。
//
// 字段:
// - BigVersion: 表示大版本号。
// - SmallVersion: 表示小版本号。
// - FixVersion: 表示修复版本号。
type VersionInfo struct {
BigVersion uint8
SmallVersion uint8
FixVersion uint8
}

167
wba/event.go Normal file
View File

@ -0,0 +1,167 @@
package wba
import "encoding/json"
// event.go 这里定义协议事件的结构体
// MessageEventInfo 消息事件结构体
type MessageEventInfo struct {
// Time 事件发生的时间戳
Time int64 `json:"time,omitempty"`
// SelfId 机器人的用户 ID
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
MessageType string `json:"message_type,omitempty"`
SubType string `json:"sub_type,omitempty"`
MessageId int32 `json:"message_id,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Anonymous AnonymousInfo `json:"anonymous"`
Message []MessageInfo `json:"message,omitempty"`
RawMessage string `json:"raw_message,omitempty"`
Font int32 `json:"font,omitempty"`
Sender SenderInfo `json:"sender"`
}
func (msg *MessageEventInfo) GetAt() []string {
var at []string
for _, v := range msg.Message {
if v.Type == "at" {
at = append(at, v.Data.Qq)
}
}
return at
}
func (msg *MessageEventInfo) GetText() string {
var text string
for _, v := range msg.Message {
if v.Type == "text" {
text += v.Data.Text
}
}
return text
}
func (msg *MessageEventInfo) JsonMarshal() string {
jsonData, err := json.Marshal(msg)
if err != nil {
return ""
}
return string(jsonData)
}
type NoticeEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
NoticeType string `json:"notice_type,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
File FileInfo `json:"file,omitempty"`
SubType string `json:"sub_type,omitempty"`
OperatorId int64 `json:"operator_id,omitempty"`
Duration int64 `json:"duration,omitempty"`
MessageId int64 `json:"message,omitempty"`
TargetId int64 `json:"target_id,omitempty"`
HonorType string `json:"honor_type,omitempty"`
}
type RequestEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
RequestType string `json:"request_type,omitempty"`
SubType string `json:"sub_type,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Comment string `json:"comment,omitempty"`
Flag string `json:"flag,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
}
type MetaEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
MetaEventType string `json:"meta_event_type,omitempty"`
SubType string `json:"sub_type,omitempty"`
Status string `json:"status,omitempty"`
Interval int64 `json:"interval,omitempty"`
}
type FileInfo struct {
Id string `json:"id,omitempty"`
Name string `json:"Name,omitempty"`
Size int64 `json:"size,omitempty"`
Busid int64 `json:"bucket,omitempty"`
}
type SenderInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Card string `json:"card,omitempty"`
Sex string `json:"sex,omitempty"`
Age int32 `json:"age,omitempty"`
Area string `json:"area,omitempty"`
Level string `json:"level,omitempty"`
Role string `json:"role,omitempty"`
Title string `json:"title,omitempty"`
}
type AnonymousInfo struct {
Id string `json:"id,omitempty"`
Name string `json:"Name,omitempty"`
Flag string `json:"flag,omitempty"`
}
type MessageInfo struct {
Type string `json:"type,omitempty"`
Data MessageDataInfo `json:"data"`
}
type MessageDataInfo struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
Id string `json:"id,omitempty"`
File string `json:"file,omitempty"`
Url string `json:"url,omitempty"`
Magic string `json:"magic,omitempty"`
Qq string `json:"qq,omitempty"`
Title string `json:"title,omitempty"`
Content any `json:"content,omitempty"` // Content string or []MessageDataInfo
Image string `json:"image,omitempty"`
Audio string `json:"audio,omitempty"`
Lat string `json:"lat,omitempty"`
Lon string `json:"lon,omitempty"`
Data string `json:"data,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"name,omitempty"`
}
type ParamsInfo struct {
Message string `json:"message,omitempty"`
UserId int64 `json:"user_id,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
AutoEscape bool `json:"auto_escape,omitempty"`
MessageId int32 `json:"message_id,omitempty"`
Id string `json:"id,omitempty"`
RejectAddRequest bool `json:"reject_add_request,omitempty"`
Duration int32 `json:"duration,omitempty"`
Enable bool `json:"enable,omitempty"`
Card string `json:"card,omitempty"`
GroupName string `json:"group_name,omitempty"`
IsDismiss bool `json:"is_dismiss,omitempty"`
SpecialTitle string `json:"special_title,omitempty"`
Flag string `json:"flag,omitempty"`
Approve bool `json:"approve,omitempty"`
Remark string `json:"remark,omitempty"`
Type string `json:"type,omitempty"`
SubType string `json:"sub_type,omitempty"`
Reason string `json:"reason,omitempty"`
NoCache bool `json:"no_cache,omitempty"`
File string `json:"file,omitempty"`
Times int `json:"times,omitempty"`
Domain string `json:"domain,omitempty"`
OutFormat string `json:"out_format,omitempty"`
Delay int32 `json:"delay,omitempty"`
}

View File

@ -1,6 +1,6 @@
# WBA 文档
WBA是用于对接WIND核心和APP通信的接口规定了应用结构规范并提供了与wind核心交互的接口定义。
WBA是用于对接Bot协议、WIND核心和APP通信的接口规定了应用结构规范并提供了与wind核心交互的接口定义。
## 目录
- [1. 应用结构规范](#1-应用结构规范)

360
wba/selector.go Normal file
View File

@ -0,0 +1,360 @@
package wba
import (
"ProjectWIND/LOG"
"encoding/json"
"fmt"
)
type CmdAgentSelector struct {
Session SessionInfo
Selectors [4]map[SelectorLabel]Selector
cmdTable map[CmdLabel]map[CmdSetLabel]struct{}
cmdSetTable map[CmdSetLabel]map[OptionLabel]struct{}
optionTable map[OptionLabel][]map[SelectorLabel]struct{}
}
type Selector struct {
Level Priority
Name SelectorLabel
Options map[OptionLabel]Option
currentOption OptionLabel
}
type Option struct {
Name string
CmdSets map[CmdSetLabel]CmdSet
}
type CmdSet struct {
Name CmdSetLabel
Cmds map[CmdLabel]CmdInfo
Enabled bool
}
type CmdInfo struct {
Name CmdLabel
Cmd Cmd
Enabled bool
}
// NewCmdAgentSelector 创建一个新的 CmdAgentSelector 实例并初始化。
//
// 参数:
//
// - session: 所属会话信息。
func NewCmdAgentSelector() CmdAgentSelector {
c := CmdAgentSelector{
Session: SessionInfo{},
Selectors: [4]map[SelectorLabel]Selector{},
cmdTable: make(map[CmdLabel]map[CmdSetLabel]struct{}),
cmdSetTable: make(map[CmdSetLabel]map[OptionLabel]struct{}),
optionTable: make(map[OptionLabel][]map[SelectorLabel]struct{}),
}
for i := 0; i < 4; i++ {
c.Selectors[i] = make(map[SelectorLabel]Selector)
c.optionTable[fmt.Sprintf("%d", i)] = make([]map[SelectorLabel]struct{}, 4)
}
return c
}
func (a *CmdAgentSelector) LoadConfig(session SessionInfo, config string) error {
a.Session = session
conf := CmdAgentSelector{}
err := json.Unmarshal([]byte(config), &conf)
if err != nil {
return err
}
for i := 0; i < 4; i++ {
for selectorName, selector := range a.Selectors[i] {
selector.currentOption = conf.Selectors[i][selectorName].currentOption
for optionName, option := range selector.Options {
for cmdSetName, cmdSet := range option.CmdSets {
for cmdName, cmd := range cmdSet.Cmds {
cmd.Enabled = conf.Selectors[i][selectorName].Options[optionName].CmdSets[cmdSetName].Cmds[cmdName].Enabled
}
}
}
}
}
return nil
}
// AddCmdMap 添加一个命令映射(map[AppKey]CmdList)到 CmdAgentSelector 中。
//
// 参数:
//
// - cmdMap: 命令映射,键为 AppKey值为 CmdList。
func (a *CmdAgentSelector) AddCmdMap(cmdMap map[AppKey]CmdList) {
for appKey, cmds := range cmdMap {
a.AddCmdSet(appKey, cmds)
}
}
// AddCmdSet 添加一个命令集(即APP)到 CmdAgentSelector 中。
//
// 参数:
//
// - appKey: 应用键,包含应用名称、级别、选择器和选项。
// - cmds: 命令列表,键为命令名称,值为 CmdInfo。
func (a *CmdAgentSelector) AddCmdSet(appKey AppKey, cmds CmdList) {
for cmdName, cmd := range cmds {
// 确保所有必要的map已初始化
if a.cmdTable == nil {
a.cmdTable = make(map[CmdLabel]map[CmdSetLabel]struct{})
}
if a.cmdSetTable == nil {
a.cmdSetTable = make(map[CmdSetLabel]map[OptionLabel]struct{})
}
if a.optionTable == nil {
a.optionTable = make(map[OptionLabel][]map[SelectorLabel]struct{})
}
// 初始化嵌套map
if a.cmdTable[cmdName] == nil {
a.cmdTable[cmdName] = make(map[CmdSetLabel]struct{})
}
if a.cmdSetTable[appKey.Name] == nil {
a.cmdSetTable[appKey.Name] = make(map[OptionLabel]struct{})
}
if a.optionTable[appKey.Option] == nil {
a.optionTable[appKey.Option] = make([]map[SelectorLabel]struct{}, 4)
}
if a.optionTable[appKey.Option][appKey.Level] == nil {
a.optionTable[appKey.Option][appKey.Level] = make(map[SelectorLabel]struct{})
}
// 确保Selector层级的map已初始化
if a.Selectors[appKey.Level] == nil {
a.Selectors[appKey.Level] = make(map[SelectorLabel]Selector)
}
if a.Selectors[appKey.Level][appKey.Selector].Options == nil {
selector := a.Selectors[appKey.Level][appKey.Selector]
selector.Options = make(map[OptionLabel]Option)
a.Selectors[appKey.Level][appKey.Selector] = selector
}
if a.Selectors[appKey.Level][appKey.Selector].Options[appKey.Option].CmdSets == nil {
selector := a.Selectors[appKey.Level][appKey.Selector]
option := selector.Options[appKey.Option]
option.CmdSets = make(map[CmdSetLabel]CmdSet)
selector.Options[appKey.Option] = option
a.Selectors[appKey.Level][appKey.Selector] = selector
}
if a.Selectors[appKey.Level][appKey.Selector].Options[appKey.Option].CmdSets[appKey.Name].Cmds == nil {
selector := a.Selectors[appKey.Level][appKey.Selector]
option := selector.Options[appKey.Option]
cmdSet := option.CmdSets[appKey.Name]
cmdSet.Cmds = make(map[CmdLabel]CmdInfo)
option.CmdSets[appKey.Name] = cmdSet
selector.Options[appKey.Option] = option
a.Selectors[appKey.Level][appKey.Selector] = selector
}
a.cmdTable[cmdName][appKey.Name] = struct{}{}
a.cmdSetTable[appKey.Name][appKey.Option] = struct{}{}
a.optionTable[appKey.Option][appKey.Level][appKey.Selector] = struct{}{}
a.Selectors[appKey.Level][appKey.Selector].Options[appKey.Option].CmdSets[appKey.Name].Cmds[cmdName] = CmdInfo{
Cmd: cmd,
Enabled: true,
}
LOG.Debug("add cmd: %s, app: %s, option: %s, cmdSet: %s", cmdName, appKey.Name, appKey.Option, appKey.Selector)
}
a.Selectors[appKey.Level][appKey.Selector].Options[appKey.Option].CmdSets[appKey.Name] = CmdSet{
Name: appKey.Name,
Cmds: a.Selectors[appKey.Level][appKey.Selector].Options[appKey.Option].CmdSets[appKey.Name].Cmds,
Enabled: true,
}
if a.Selectors[appKey.Level][appKey.Selector].currentOption == "" {
a.Selectors[appKey.Level][appKey.Selector] = Selector{
Level: appKey.Level,
Name: appKey.Selector,
Options: a.Selectors[appKey.Level][appKey.Selector].Options,
currentOption: appKey.Option,
}
}
}
// FindCmd 根据命令名称和是否只查找启用的命令,查找符合条件的命令。
//
// 参数:
//
// - cmdName: 要查找的命令名称。
// - onlyEnabled: 是否只查找启用的命令。
//
// 返回值:
//
// - result: 找到的命令列表(越靠前的优先级越高)。
// - count: 找到的命令数量。
func (a *CmdAgentSelector) FindCmd(cmdName string, onlyEnabled bool) (result []Cmd, count int) {
result = make([]Cmd, 0)
count = 0
cmdSets := a.cmdTable[cmdName]
for cmdSet := range cmdSets {
options := a.cmdSetTable[cmdSet]
for option := range options {
selectors := a.optionTable[option]
for i := 0; i < len(a.Selectors); i++ {
for selector := range selectors[i] {
if option != a.Selectors[i][selector].currentOption && onlyEnabled {
continue
}
cmdInfo := a.Selectors[i][selector].Options[option].CmdSets[cmdSet].Cmds[cmdName]
if onlyEnabled && !cmdInfo.Enabled {
continue
}
result = append(result, cmdInfo.Cmd)
count++
}
}
}
}
return result, count
}
// FindCmdSet 根据命令集名称和是否只查找启用的命令集,查找符合条件的命令集。
//
// 参数:
//
// - cmdSetName: 要查找的命令集名称。
// - onlyEnabled: 是否只查找启用的命令集。
//
// 返回值:
//
// - result: 找到的命令集列表(越靠前的优先级越高)。
// - count: 找到的命令集数量。
func (a *CmdAgentSelector) FindCmdSet(cmdSetName string, onlyEnabled bool) (result []CmdSet, count int) {
result = make([]CmdSet, 0)
count = 0
options := a.cmdSetTable[cmdSetName]
for option := range options {
selectors := a.optionTable[option]
for i := 0; i < len(a.Selectors); i++ {
for selector := range selectors[i] {
if option != a.Selectors[i][selector].currentOption && onlyEnabled {
continue
}
cmdSet := a.Selectors[i][selector].Options[option].CmdSets[cmdSetName]
result = append(result, cmdSet)
count++
}
}
}
return result, count
}
// FindOption 根据选项名称,查找符合条件的选项。
//
// 参数:
//
// - optionName: 要查找的选项名称。
//
// 返回值:
//
// - result: 找到的选项列表(越靠前的优先级越高)。
// - count: 找到的选项数量。
func (a *CmdAgentSelector) FindOption(optionName string) (result []Option, count int) {
result = make([]Option, 0)
count = 0
selectors := a.optionTable[optionName]
for i := 0; i < len(a.Selectors); i++ {
for selector := range selectors[i] {
if optionName != a.Selectors[i][selector].currentOption {
continue
}
option := a.Selectors[i][selector].Options[optionName]
result = append(result, option)
count++
}
}
return result, count
}
// FindSelector 根据选择器名称,查找符合条件的选择器。
//
// 参数:
//
// - selectorName: 要查找的选择器名称。
//
// 返回值:
//
// - result: 找到的选择器列表(越靠前的优先级越高)。
// - count: 找到的选择器数量。
func (a *CmdAgentSelector) FindSelector(selectorName string) (result []Selector, count int) {
result = make([]Selector, 0)
count = 0
selectors := a.optionTable[selectorName]
for i := 0; i < len(a.Selectors); i++ {
for selector := range selectors[i] {
if selectorName != a.Selectors[i][selector].currentOption {
continue
}
selector := a.Selectors[i][selector]
result = append(result, selector)
count++
}
}
return result, count
}
// GetCurrentOption 获取当前选择器的当前选项。
//
// 参数:
//
// - level: 选择器的级别。
// - selectorName: 选择器的名称。
//
// 返回值:
//
// - result: 当前选项当isExist为false时此结果为 Option 对应的空值。
// - isExist: 是否存在当前选项。
func (a *CmdAgentSelector) GetCurrentOption(level int, selectorName string) (result Option, isExist bool) {
result, ok := a.Selectors[level][selectorName].Options[a.Selectors[level][selectorName].currentOption]
if !ok {
return Option{}, false
}
return result, true
}
// ToggleCmd 切换命令的启用状态。
//
// 参数:
//
// - enabled: 要设置的启用状态。
// - level: 选择器的级别。
// - selectorName: 选择器的名称。
// - optionName: 选项的名称。
// - cmdSetName: 命令集的名称。
// - cmdName: 命令的名称。
//
// 返回值:
//
// - state: 命令的启用状态。
// - preState: 命令的之前的启用状态。
// - err: 错误信息,当发生错误时返回。
func (a *CmdAgentSelector) ToggleCmd(enabled bool, level Priority, selectorName SelectorLabel, optionName OptionLabel, cmdSetName CmdSetLabel, cmdName CmdLabel) (state bool, preState bool, err error) {
if level < 0 || level > 3 {
return false, false, fmt.Errorf("level must be between 0 and 3")
}
selector, ok := a.Selectors[level][selectorName]
if !ok {
return false, false, fmt.Errorf("selector %s not found", selectorName)
}
option, ok := selector.Options[optionName]
if !ok {
return false, false, fmt.Errorf("option %s not found", optionName)
}
cmdSet, ok := option.CmdSets[cmdName]
if !ok {
return false, false, fmt.Errorf("cmdSet %s not found", cmdName)
}
cmdInfo, ok := cmdSet.Cmds[cmdName]
if !ok {
return false, false, fmt.Errorf("cmd %s not found", cmdName)
}
preState = cmdInfo.Enabled
cmdInfo.Enabled = enabled
a.Selectors[level][selectorName].Options[optionName].CmdSets[cmdSetName].Cmds[cmdName] = cmdInfo
return enabled, preState, nil
}

View File

@ -1,425 +0,0 @@
package wba
import (
"fmt"
)
type APP interface {
Get() AppInfo
Init(api WindAPI) error
}
type WindAPI interface {
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()
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
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 (ai AppInfo) Get() AppInfo {
return ai
}
func (ai *AppInfo) Init(api WindAPI) error {
Wind = api
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
}
}
func WithVersion(version string) AppInfoOption {
return func(ei *AppInfo) {
ei.Version = version
}
}
func WithAuthor(author string) AppInfoOption {
return func(ei *AppInfo) {
ei.Author = author
}
}
func WithDescription(description string) AppInfoOption {
return func(ei *AppInfo) {
ei.Description = description
}
}
func WithNamespace(namespace string) AppInfoOption {
return func(ei *AppInfo) {
ei.Namespace = namespace
}
}
func WithWebUrl(webUrl string) AppInfoOption {
return func(ei *AppInfo) {
ei.Homepage = webUrl
}
}
func WithLicense(license string) AppInfoOption {
return func(ei *AppInfo) {
ei.License = license
}
}
func WithAppType(appType string) AppInfoOption {
return func(ei *AppInfo) {
ei.AppType = appType
}
}
func WithRule(rule string) AppInfoOption {
return func(ei *AppInfo) {
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: "PUBLIC",
Homepage: "https://github.com/Sheyiyuan/wind_app_model",
License: "MIT",
AppType: "fun",
Rule: "none",
CmdMap: make(map[string]Cmd),
}
for _, opt := range opts {
opt(&Ext)
}
return Ext
}
func NewCmd(name string, description string, solve func(args []string, msg MessageEventInfo)) Cmd {
return Cmd{
NAME: name,
DESC: description,
SOLVE: solve,
}
}
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
SOLVE func(args []string, msg MessageEventInfo)
}
type MessageEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
MessageType string `json:"message_type,omitempty"`
SubType string `json:"sub_type,omitempty"`
MessageId int32 `json:"message_id,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Anonymous AnonymousInfo `json:"anonymous"`
Message []MessageInfo `json:"message,omitempty"`
RawMessage string `json:"raw_message,omitempty"`
Font int32 `json:"font,omitempty"`
Sender SenderInfo `json:"sender"`
}
type NoticeEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
NoticeType string `json:"notice_type,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
File FileInfo `json:"file,omitempty"`
SubType string `json:"sub_type,omitempty"`
OperatorId int64 `json:"operator_id,omitempty"`
Duration int64 `json:"duration,omitempty"`
MessageId int64 `json:"message,omitempty"`
TargetId int64 `json:"target_id,omitempty"`
HonorType string `json:"honor_type,omitempty"`
}
type RequestEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
RequestType string `json:"request_type,omitempty"`
SubType string `json:"sub_type,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Comment string `json:"comment,omitempty"`
Flag string `json:"flag,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
}
type MetaEventInfo struct {
Time int64 `json:"time,omitempty"`
SelfId int64 `json:"self_id,omitempty"`
PostType string `json:"post_type,omitempty"`
MetaEventType string `json:"meta_event_type,omitempty"`
SubType string `json:"sub_type,omitempty"`
Status string `json:"status,omitempty"`
Interval int64 `json:"interval,omitempty"`
}
type FileInfo struct {
Id string `json:"id,omitempty"`
Name string `json:"Name,omitempty"`
Size int64 `json:"size,omitempty"`
Busid int64 `json:"bucket,omitempty"`
}
type SenderInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Card string `json:"card,omitempty"`
Sex string `json:"sex,omitempty"`
Age int32 `json:"age,omitempty"`
Area string `json:"area,omitempty"`
Level string `json:"level,omitempty"`
Role string `json:"role,omitempty"`
Title string `json:"title,omitempty"`
}
type AnonymousInfo struct {
Id string `json:"id,omitempty"`
Name string `json:"Name,omitempty"`
Flag string `json:"flag,omitempty"`
}
type MessageInfo struct {
Type string `json:"type,omitempty"`
Data MessageDataInfo `json:"data"`
}
type MessageDataInfo struct {
Type string `json:"type,omitempty"`
Text string `json:"text,omitempty"`
Id string `json:"id,omitempty"`
File string `json:"file,omitempty"`
Url string `json:"url,omitempty"`
Magic string `json:"magic,omitempty"`
Qq string `json:"qq,omitempty"`
Title string `json:"title,omitempty"`
Content string `json:"content,omitempty"`
Image string `json:"image,omitempty"`
Audio string `json:"audio,omitempty"`
Lat string `json:"lat,omitempty"`
Lon string `json:"lon,omitempty"`
Data string `json:"data,omitempty"`
}
type ParamsInfo struct {
Message string `json:"message,omitempty"`
UserId int64 `json:"user_id,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
AutoEscape bool `json:"auto_escape,omitempty"`
MessageId int32 `json:"message_id,omitempty"`
Id string `json:"id,omitempty"`
RejectAddRequest bool `json:"reject_add_request,omitempty"`
Duration int32 `json:"duration,omitempty"`
Enable bool `json:"enable,omitempty"`
Card string `json:"card,omitempty"`
GroupName string `json:"group_name,omitempty"`
IsDismiss bool `json:"is_dismiss,omitempty"`
SpecialTitle string `json:"special_title,omitempty"`
Flag string `json:"flag,omitempty"`
Approve bool `json:"approve,omitempty"`
Remark string `json:"remark,omitempty"`
Type string `json:"type,omitempty"`
SubType string `json:"sub_type,omitempty"`
Reason string `json:"reason,omitempty"`
NoCache bool `json:"no_cache,omitempty"`
File string `json:"file,omitempty"`
Times int `json:"times,omitempty"`
Domain string `json:"domain,omitempty"`
OutFormat string `json:"out_format,omitempty"`
Delay int32 `json:"delay,omitempty"`
}
type APIRequestInfo struct {
Action string `json:"action,omitempty"`
Params ParamsInfo `json:"params"`
Echo string `json:"echo,omitempty"`
}
type APIResponseInfo struct {
Status string `json:"status,omitempty"`
Retcode int64 `json:"retcode,omitempty"`
Data ResponseDataInfo `json:"data,omitempty"`
Echo string `json:"echo,omitempty"`
}
type APIResponseListInfo struct {
Status string `json:"status,omitempty"`
Retcode int64 `json:"retcode,omitempty"`
Data []ResponseDataInfo `json:"data,omitempty"`
Echo string `json:"echo,omitempty"`
}
type ResponseDataInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Sex string `json:"sex,omitempty"`
Age int32 `json:"age,omitempty"`
Remark string `json:"remark,omitempty"`
GroupId int64 `json:"group_id,omitempty"`
GroupName string `json:"group_name,omitempty"`
MemberCount int32 `json:"member_count,omitempty"`
MaxMemberCount int32 `json:"max_member_count,omitempty"`
Card string `json:"card,omitempty"`
Area string `json:"area,omitempty"`
JoinTime int32 `json:"join_time,omitempty"`
LastSentTime int32 `json:"last_sent_time,omitempty"`
Level string `json:"level,omitempty"`
Role string `json:"role,omitempty"`
Unfriendly bool `json:"unfriendly,omitempty"`
Title string `json:"title,omitempty"`
TitleExpireTime int32 `json:"title_expire_time,omitempty"`
CardChangeable bool `json:"card_changeable,omitempty"`
CurrentTalkative CurrentTalkativeInfo `json:"current_talkative,omitempty"`
TalkativeList []CurrentTalkativeInfo `json:"talkative_list,omitempty"`
PerformerList []HonorInfo `json:"performer_list,omitempty"`
LegendList []HonorInfo `json:"legend_list,omitempty"`
StrongNewbieList []HonorInfo `json:"strong_newbie_list,omitempty"`
EmoticonList []HonorInfo `json:"emoticon_list,omitempty"`
Cookies string `json:"cookies,omitempty"`
Token string `json:"token,omitempty"`
CsrfToken string `json:"csrf_token,omitempty"`
File string `json:"file,omitempty"`
OutFormat string `json:"out_format,omitempty"`
Yes bool `json:"yes,omitempty"`
Online bool `json:"online,omitempty"`
Good bool `json:"good,omitempty"`
AppName string `json:"app_name,omitempty"`
AppVersion string `json:"app_version,omitempty"`
ProtocolVersion string `json:"protocol_version,omitempty"`
Time int64 `json:"time,omitempty"`
MessageType string `json:"message_type,omitempty"`
MessageId int32 `json:"message_id,omitempty"`
RealId int32 `json:"real_id,omitempty"`
Sender SenderInfo `json:"sender,omitempty"`
Message []MessageDataInfo `json:"message,omitempty"`
}
type CurrentTalkativeInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Avatar string `json:"avatar,omitempty"`
DayCount int32 `json:"day_count,omitempty"`
}
type HonorInfo struct {
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Avatar string `json:"avatar,omitempty"`
Description string `json:"Description,omitempty"`
}
// SegmentInfo 消息段
type SegmentInfo struct {
Type string `json:"type,omitempty"`
Data SegmentDataInfo `json:"data,omitempty"`
}
type SegmentDataInfo struct {
Type string `json:"type,omitempty"`
QQ string `json:"qq,omitempty"`
Id int64 `json:"id,omitempty"`
UserId int64 `json:"user_id,omitempty"`
Nickname string `json:"nickname,omitempty"`
Content string `json:"content,omitempty"`
Url string `json:"url,omitempty"`
Lat string `json:"lat,omitempty"`
Lon string `json:"lon,omitempty"`
Title string `json:"title,omitempty"`
Audio string `json:"audio,omitempty"`
Image string `json:"image,omitempty"`
Video string `json:"video,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

View File

@ -0,0 +1,462 @@
package wba
// WindStandardProtocolAPI Wind标准协议API,提供了onebot11标准中的API接口。
type WindStandardProtocolAPI interface {
// UnsafelySendMsg [不安全][需要master权限]按照QQ号或群号发送消息到指定的群组或用户。
// 参数:
// - msgType: 消息类型,如 "group" 或 "private"。
// - groupId: 群组 ID若为私聊则为 0。
// - userId: 用户 ID若为群聊则为 0。
// - message: 要发送的消息内容。
// - autoEscape: 消息内容是否作为纯文本发送(即不解析 CQ 码)。
UnsafelySendMsg(msgType string, groupId int64, userId int64, message string, autoEscape bool)
// UnsafelySendPrivateMsg [不安全][需要master权限]按照QQ号发送私聊消息给指定用户。
// 参数:
// - userId: 用户 ID。
// - message: 要发送的消息内容。
// - autoEscape: 消息内容是否作为纯文本发送(即不解析 CQ 码)。
UnsafelySendPrivateMsg(userId int64, message string, autoEscape bool)
// UnsafelySendGroupMsg [不安全][需要master权限]按照群号发送群聊消息到指定群组。
// 参数:
// - groupId: 群组 ID。
// - message: 要发送的消息内容。
// - autoEscape: 消息内容是否作为纯文本发送(即不解析 CQ 码)。
UnsafelySendGroupMsg(groupId int64, message string, autoEscape bool)
// SendMsg 按照提供的消息事件信息回复指定消息。
// 参数:
// - msg: 消息事件信息。
// - message: 要回复的消息内容。
// - autoEscape: 是否自动转义消息内容。
SendMsg(msg MessageEventInfo, message string, autoEscape bool)
// SendPrivateMsg 按照提供的消息事件信息回复私聊消息。
// 参数:
// - msg: 消息事件信息。
// - message: 要回复的消息内容。
// - autoEscape: 消息内容是否作为纯文本发送(即不解析 CQ 码)。
SendPrivateMsg(msg MessageEventInfo, message string, autoEscape bool)
// SendGroupMsg 按照提供的消息事件信息回复群聊消息。
// 参数:
// - msg: 消息事件信息。
// - message: 要回复的消息内容。
// - autoEscape: 消息内容是否作为纯文本发送(即不解析 CQ 码)。
SendGroupMsg(msg MessageEventInfo, message string, autoEscape bool)
// UnsafelyDeleteMsg [不安全][需要master权限]撤回指定消息。
// 参数:
// - msgId: 消息 ID。
UnsafelyDeleteMsg(msgId int32)
// DeleteMsg 按照提供的消息事件信息撤回指定的消息。
// 参数:
// - msg: 消息事件信息。
DeleteMsg(msg MessageEventInfo)
// SendLike 给指定用户发送点赞,支持指定点赞次数。
// 参数:
// - userId: 用户 ID。
// - times: 赞的次数,每个好友每天最多 10 次。
SendLike(userId int64, times int)
// SetGroupKick 将指定用户踢出指定群组,支持拒绝该用户再次加入。
// 参数:
// - groupId: 群组 ID。
// - userId: 用户 ID。
// - rejectAddRequest: 是否拒绝该用户再次加入。
SetGroupKick(groupId int64, userId int64, rejectAddRequest bool)
// SetGroupBan 禁言指定用户在指定群组的发言,支持指定禁言时长。
// 参数:
// - groupId: 群组 ID。
// - userId: 用户 ID。
// - duration: 禁言时长单位为秒0 表示取消禁言。
SetGroupBan(groupId int64, userId int64, duration int32)
// SetGroupWholeBan 开启或关闭指定群组的全员禁言。
// 参数:
// - groupId: 群组 ID。
// - enable: 是否开启全员禁言。
SetGroupWholeBan(groupId int64, enable bool)
// SetGroupAdmin 设置或取消指定用户在指定群组的管理员权限。
// 参数:
// - groupId: 群组 ID。
// - userId: 用户 ID。
// - enable: 是否设置为管理员。
SetGroupAdmin(groupId int64, userId int64, enable bool)
// SetGroupLeave 退出指定群组。
// 参数:
// - groupId: 群组 ID。
// - isDismiss: 是否解散,如果登录号是群主,则仅在此项为 true 时能够解散。
SetGroupLeave(groupId int64, isDismiss bool)
// SetGroupCard 设置指定用户在指定群组的群名片。
// 参数:
// - groupId: 群组 ID。
// - userId: 用户 ID。
// - card: 群名片内容。
SetGroupCard(groupId int64, userId int64, card string)
// SetGroupName 设置指定群组的名称。
// 参数:
// - groupId: 群组 ID。
// - groupName: 群组名称。
SetGroupName(groupId int64, groupName string)
// SetGroupSpecialTitle 设置指定用户在指定群组的专属头衔,支持指定头衔有效期。
// 参数:
// - groupId: 群组 ID。
// - userId: 用户 ID。
// - specialTitle: 专属头衔内容。
SetGroupSpecialTitle(groupId int64, userId int64, specialTitle string)
// SetFriendAddRequest 处理好友添加请求,支持同意或拒绝并添加备注。
// 参数:
// - flag: 请求标识(需从上报的数据中获得)。
// - approve: 是否同意请求。
// - remark: 备注信息(仅在同意时有效)。
SetFriendAddRequest(flag string, approve bool, remark string)
// SetGroupAddRequest 处理群组添加请求,支持同意或拒绝并添加理由。
// 参数:
// - flag: 请求标识。
// - subType: 请求子类型。
// - approve: 是否同意请求。
// - reason: 拒绝理由。
SetGroupAddRequest(flag string, subType string, approve bool, reason string)
// GetLoginInfo 获取当前登录的账号信息。
// 返回: API 响应信息。
GetLoginInfo() APIResponseInfo
// GetVersionInfo 获取当前程序的版本信息。
// 返回: API 响应信息。
GetVersionInfo() APIResponseInfo
// GetMsg 获取指定消息 ID 的消息内容。
// 参数:
// - msgId: 消息 ID。
// 返回: API 响应信息。
GetMsg(msgId int32) APIResponseInfo
// GetForwardMsg 获取指定转发消息 ID 的消息内容。
// 参数:
// - msgId: 转发消息 ID。
// 返回: API 响应信息。
GetForwardMsg(msgId string) APIResponseInfo
// GetGroupList 获取当前登录账号加入的所有群组列表。
// 返回: API 响应信息。
GetGroupList() APIResponseInfo
// GetGroupMemberList 获取指定群组的所有成员列表。
// 参数:
// - groupId: 群组 ID。
// 返回: API 响应信息。
GetGroupMemberList(groupId int64) APIResponseInfo
// GetGroupMemberInfo 获取指定群组中指定用户的详细信息,支持缓存控制。
// 参数:
// - groupId: 群组 ID。
// - userId: 用户 ID。
// - noCache: 是否不使用缓存。
// 返回: API 响应信息。
GetGroupMemberInfo(groupId int64, userId int64, noCache bool) APIResponseInfo
// GetFriendList 获取当前登录账号的所有好友列表。
// 返回: API 响应信息。
GetFriendList() APIResponseInfo
// GetStrangerInfo 获取指定用户的详细信息,支持缓存控制。
// 参数:
// - userId: 用户 ID。
// - noCache: 是否不使用缓存。
// 返回: API 响应信息。
GetStrangerInfo(userId int64, noCache bool) APIResponseInfo
// GetGroupInfo 获取指定群组的详细信息,支持缓存控制。
// 参数:
// - groupId: 群组 ID。
// - noCache: 是否不使用缓存。
// 返回: API 响应信息。
GetGroupInfo(groupId int64, noCache bool) APIResponseInfo
// GetGroupHonorInfo 获取指定群组的荣誉信息。
// 参数:
// - groupId: 群组 ID。
// - Type: 荣誉类型。
// 返回: API 响应信息。
GetGroupHonorInfo(groupId int64, Type string) APIResponseInfo
// GetStatus 获取当前程序的运行状态信息。
// 返回: API 响应信息。
GetStatus() APIResponseInfo
// // GetCookies 获取指定域名的 Cookies 信息。
// 参数:
// - domain: 域名。
// 返回: API 响应信息。
GetCookies(domain string) APIResponseInfo
// GetCSRFToken 获取 CSRF 令牌。
// 返回: API 响应信息。
GetCSRFToken() APIResponseInfo
// GetCredentials 获取指定域名的凭证信息。
// 参数:
// - domain: 域名。
// 返回: API 响应信息。
GetCredentials(domain string) APIResponseInfo
// GetImage 获取指定文件的图片信息。
// 参数:
// - file: 文件路径或标识符。
// 返回: API 响应信息。
GetImage(file string) APIResponseInfo
// GetRecord 获取指定文件的语音记录信息,支持指定输出格式。
// 参数:
// - file: 文件路径或标识符。
// - outFormat: 输出格式。
// 返回: API 响应信息。
GetRecord(file string, outFormat string) APIResponseInfo
// CanSendImage 检查是否可以发送图片。
// 返回: API 响应信息。
CanSendImage() APIResponseInfo
// CanSendRecord 检查是否可以发送语音记录。
// 返回: API 响应信息。
CanSendRecord() APIResponseInfo
// SetRestart 设置程序在指定延迟后重启。
// 参数:
// - delay: 延迟时间,单位为秒。
SetRestart(delay int32)
// CleanCache 清理程序的缓存。
CleanCache()
}
type WindStandardDataBaseAPI interface {
// SetUserVariable 设置用户变量
// 参数:
// - app: 应用信息。
// - msg: 消息事件信息。
// - key: 变量名称。
// - value: 变量值。
SetUserVariable(app AppInfo, msg MessageEventInfo, key string, value string)
// SetGroupVariable 设置群组变量
// 参数:
// - app: 应用信息。
// - msg: 消息事件信息。
// - key: 变量名称。
// - value: 变量值。
SetGroupVariable(app AppInfo, msg MessageEventInfo, key string, value string)
// SetOutUserVariable [需要master权限]设置其他数据库中的用户变量
// 参数:
// - app: 应用信息。
// - msg: 消息事件信息。
// - key: 变量名称。
// - value: 变量值。
// - datamap: 数据表名称。
SetOutUserVariable(app AppInfo, datamap string, msg MessageEventInfo, key string, value string)
// SetOutGroupVariable [需要master权限]设置其他数据库中的群组变量
// 参数:
// - app: 应用信息。
// - msg: 消息事件信息。
// - key: 变量名称。
// - value: 变量值。
// - datamap: 数据表名称。
SetOutGroupVariable(app AppInfo, datamap string, msg MessageEventInfo, key string, value string)
// UnsafelySetUserVariable [不安全][需要master权限]设置用户变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
UnsafelySetUserVariable(app AppInfo, id string, key string, value string)
// UnsafelySetGroupVariable [不安全][需要master权限]设置群组变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
UnsafelySetGroupVariable(app AppInfo, id string, key string, value string)
// UnsafelySetGlobalVariable [不安全][需要master权限]设置全局变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
UnsafelySetGlobalVariable(app AppInfo, id string, key string, value string)
// UnsafelySetOutUserVariable [不安全][需要master权限]设置其他数据库中的用户变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
// - datamap: 数据表名称。
UnsafelySetOutUserVariable(app AppInfo, datamap string, id string, key string, value string)
// UnsafelySetOutGroupVariable [不安全][需要master权限]设置其他数据库中的群组变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
// - datamap: 数据表名称。
UnsafelySetOutGroupVariable(app AppInfo, datamap string, id string, key string, value string)
// UnsafelySetOutGlobalVariable [不安全][需要master权限]设置其他数据库中的全局变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - value: 变量值。
// - datamap: 数据表名称。
UnsafelySetOutGlobalVariable(app AppInfo, datamap string, id string, key string, value string)
// GetUserVariable 获取用户变量
// 参数:
// - app: 应用信息。
// - msg: 消息变量名称。
// - key: 变量名称。
// 返回: 变量值,是否存在。
GetUserVariable(app AppInfo, msg MessageEventInfo, key string) (string, bool)
// GetGroupVariable 获取群组变量
// 参数:
// - app: 应用信息。
// - msg: 消息变量名称。
// - key: 变量名称。
// 返回: 变量值,是否存在。
GetGroupVariable(app AppInfo, msg MessageEventInfo, key string) (string, bool)
// GetOutUserVariable [需要master权限]获取其他数据库中的用户变量
// 参数:
// - app: 应用信息。
// - msg: 消息变量名称。
// - key: 变量名称。
// - datamap数据表名称。
// 返回: 变量值,是否存在。
GetOutUserVariable(app AppInfo, datamap string, msg MessageEventInfo, key string) (string, bool)
// GetOutGroupVariable [需要master权限]获取其他数据库中的群组变量
// 参数:
// - app: 应用信息。
// - msg: 消息变量名称。
// - key: 变量名称。
// - datamap数据表名称。
// 返回: 变量值,是否存在。
GetOutGroupVariable(app AppInfo, datamap string, msg MessageEventInfo, key string) (string, bool)
// UnsafelyGetUserVariable [不安全][需要master权限]获取用户变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// 返回: 变量值,是否存在。
UnsafelyGetUserVariable(app AppInfo, id string, key string) (string, bool)
// UnsafelyGetGroupVariable [不安全][需要master权限]获取群组变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// 返回: 变量值,是否存在。
UnsafelyGetGroupVariable(app AppInfo, id string, key string) (string, bool)
// UnsafelyGetGlobalVariable [不安全][需要master权限]获取全局变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// 返回: 变量值,是否存在。
UnsafelyGetGlobalVariable(app AppInfo, id string, key string) (string, bool)
// UnsafelyGetOutUserVariable [不安全][需要master权限]获取其他数据库中的用户变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - datamap数据表名称。
// 返回: 变量值,是否存在。
UnsafelyGetOutUserVariable(app AppInfo, datamap string, id string, key string) (string, bool)
// UnsafelyGetOutGroupVariable [不安全][需要master权限]获取其他数据库中的群组变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - datamap数据表名称。
// 返回: 变量值,是否存在。
UnsafelyGetOutGroupVariable(app AppInfo, datamap string, id string, key string) (string, bool)
// UnsafelyGetOutGlobalVariable [不安全][需要master权限]获取其他数据库中的全局变量
// 参数:
// - app: 应用信息。
// - id: 数据单元 ID。
// - key: 变量名称。
// - datamap数据表名称。
// 返回: 变量值,是否存在。
UnsafelyGetOutGlobalVariable(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)
// UnsafelyCreatePublicDatamap [不安全][需要master权限]创建公共数据表
// 参数:
// - app: 应用信息。
// - datamapId: 数据表名称。
UnsafelyCreatePublicDatamap(app AppInfo, datamapId string)
}

View File

@ -0,0 +1,62 @@
package wba
import (
"fmt"
)
type WindStandardTools interface {
// MsgUnmarshal 解析消息JSON字符串为 MessageEventInfo 结构体。
//
// 参数:
//
// - message: 要解析的消息JSON字符串。
//
// 返回值:
//
// - msg: 解析后的消息结构体。
MsgUnmarshal(message string) (msg MessageEventInfo)
// LogWith 使用指定日志级别记录日志,支持可变参数占位符。
//
// 参数:
// - level: 日志级别: "trace", "debug", "info", "notice", "warn", "error"。
// - log: 日志内容。
// - args: 可变参数,用于格式化日志内容。
LogWith(level string, log string, args ...interface{})
// Log 记录日志,级别为 "info",支持可变参数占位符。
//
// 参数:
// - log: 日志内容。
// - args: 可变参数,用于格式化日志内容。
Log(log string, args ...interface{})
// VersionLabelAnalysis 解析版本标签为 VersionInfo 结构体。
//
// 参数:
// - versionLabel: 版本标签字符串,格式为 "大版本.小版本.修复版本"。
// 返回值:
// - versionInfo: 解析后的版本信息结构体。
VersionLabelAnalysis(versionLabel VersionLabel) (versionInfo VersionInfo)
// VersionCompare 比较两个版本标签的大小。
//
// 参数:
// - version1: 第一个版本标签字符串。
// - version2: 第二个版本标签字符串。
// 返回值:
// - result: 比较结果,-1表示version1小于version20表示version1等于version21表示version1大于version2。
VersionCompare(version1, version2 VersionLabel) (result int)
// SessionLabelAnalysis 解析会话标签为 SessionInfo 结构体。
//
// 参数:
// - sessionLabel: 会话标签字符串,格式为 "平台:会话类型-会话ID"。
// 返回值:
// - sessionInfo: 解析后的会话信息结构体。
SessionLabelAnalysis(sessionLabel SessionLabel) (sessionInfo SessionInfo)
}
func (v VersionInfo) String() string {
return fmt.Sprintf("%d.%d.%d", v.BigVersion, v.SmallVersion, v.FixVersion)
}

BIN
webui/index.html.hertz.gz Normal file

Binary file not shown.