Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
710aa53562 | ||
|
|
6d127d1492 | ||
|
|
c1cb14b938 | ||
|
|
3fcbbd3308 | ||
|
|
a94c6e37a0 | ||
|
|
5a1ede0646 | ||
|
|
c40dbead3e | ||
|
|
cf03704ea8 | ||
|
|
e2029e48c5 | ||
|
|
e4af82d73e | ||
|
|
f7c5ef21af | ||
|
|
a1e3af7953 | ||
|
|
e6c0bfe2cc | ||
|
|
7e545cef95 | ||
|
|
3fbaf7aeb6 | ||
|
|
3bc95e1317 | ||
|
|
cab6b2633e |
@@ -1,19 +1,44 @@
|
|||||||
package current
|
package current
|
||||||
|
|
||||||
import "go-wechat/model"
|
import (
|
||||||
|
"go-wechat/model"
|
||||||
|
plugin "go-wechat/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
var robotInfo model.RobotUserInfo
|
// robotInfo
|
||||||
|
// @description: 机器人信息
|
||||||
|
type robotInfo struct {
|
||||||
|
info model.RobotUserInfo
|
||||||
|
MessageHandler plugin.MessageHandler // 启用的插件
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前接入的机器人信息
|
||||||
|
var ri robotInfo
|
||||||
|
|
||||||
// SetRobotInfo
|
// SetRobotInfo
|
||||||
// @description: 设置机器人信息
|
// @description: 设置机器人信息
|
||||||
// @param info
|
// @param info
|
||||||
func SetRobotInfo(info model.RobotUserInfo) {
|
func SetRobotInfo(info model.RobotUserInfo) {
|
||||||
robotInfo = info
|
ri.info = info
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRobotInfo
|
// GetRobotInfo
|
||||||
// @description: 获取机器人信息
|
// @description: 获取机器人信息
|
||||||
// @return model.RobotUserInfo
|
// @return model.RobotUserInfo
|
||||||
func GetRobotInfo() model.RobotUserInfo {
|
func GetRobotInfo() model.RobotUserInfo {
|
||||||
return robotInfo
|
return ri.info
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRobotMessageHandler
|
||||||
|
// @description: 获取机器人插件信息
|
||||||
|
// @return robotInfo
|
||||||
|
func GetRobotMessageHandler() plugin.MessageHandler {
|
||||||
|
return ri.MessageHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRobotMessageHandler
|
||||||
|
// @description: 设置机器人插件信息
|
||||||
|
// @param handler
|
||||||
|
func SetRobotMessageHandler(handler plugin.MessageHandler) {
|
||||||
|
ri.MessageHandler = handler
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ wechat:
|
|||||||
host: 10.0.0.73:19088
|
host: 10.0.0.73:19088
|
||||||
# 是否在启动的时候自动设置hook服务的回调
|
# 是否在启动的时候自动设置hook服务的回调
|
||||||
autoSetCallback: false
|
autoSetCallback: false
|
||||||
# 回调IP,如果是Docker运行,本参数必填,如果Docker修改了映射,格式为 ip:port
|
# 回调IP,如果是Docker运行,本参数必填(填auto表示自动,不适用于 docker 环境),如果Docker修改了映射,格式为 ip:port
|
||||||
callback: 10.0.0.51
|
callback: 10.0.0.51
|
||||||
# 转发到其他地址
|
# 转发到其他地址
|
||||||
forward:
|
forward:
|
||||||
@@ -50,3 +50,7 @@ resource:
|
|||||||
welcome-new:
|
welcome-new:
|
||||||
type: emotion
|
type: emotion
|
||||||
path: 58e4150be2bba8f7b71974b10391f9e9
|
path: 58e4150be2bba8f7b71974b10391f9e9
|
||||||
|
# 水群排行榜词云,只能是图片,末尾的`\%s`也是必须的
|
||||||
|
wordcloud:
|
||||||
|
type: image
|
||||||
|
path: D:\Share\wordcloud\%s
|
||||||
|
|||||||
13
entity/plugindata.go
Normal file
13
entity/plugindata.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
// PluginData
|
||||||
|
// @description: 插件数据
|
||||||
|
type PluginData struct {
|
||||||
|
UserId string `json:"userId"` // 用户Id
|
||||||
|
PluginCode string `json:"pluginCode"` // 插件编码
|
||||||
|
Data string `json:"data"` // 数据
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PluginData) TableName() string {
|
||||||
|
return "t_plugin_data"
|
||||||
|
}
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/sashabaranov/go-openai"
|
|
||||||
"go-wechat/client"
|
|
||||||
"go-wechat/config"
|
|
||||||
"go-wechat/entity"
|
|
||||||
"go-wechat/model"
|
|
||||||
"go-wechat/utils"
|
|
||||||
"log"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// handleAtMessage
|
|
||||||
// @description: 处理At机器人的消息
|
|
||||||
// @param m
|
|
||||||
func handleAtMessage(m model.Message) {
|
|
||||||
if !config.Conf.Ai.Enable {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取出所有启用了AI的好友或群组
|
|
||||||
var count int64
|
|
||||||
client.MySQL.Model(&entity.Friend{}).Where("enable_ai IS TRUE").Where("wxid = ?", m.FromUser).Count(&count)
|
|
||||||
if count < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 预处理一下发送的消息,用正则去掉@机器人的内容
|
|
||||||
re := regexp.MustCompile(`@([^ ]+)`)
|
|
||||||
matches := re.FindStringSubmatch(m.Content)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
// 过滤掉第一个匹配到的
|
|
||||||
m.Content = strings.Replace(m.Content, matches[0], "", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组装消息体
|
|
||||||
messages := make([]openai.ChatCompletionMessage, 0)
|
|
||||||
if config.Conf.Ai.Personality != "" {
|
|
||||||
// 填充人设
|
|
||||||
messages = append(messages, openai.ChatCompletionMessage{
|
|
||||||
Role: openai.ChatMessageRoleSystem,
|
|
||||||
Content: config.Conf.Ai.Personality,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 填充用户消息
|
|
||||||
messages = append(messages, openai.ChatCompletionMessage{
|
|
||||||
Role: openai.ChatMessageRoleUser,
|
|
||||||
Content: m.Content,
|
|
||||||
})
|
|
||||||
|
|
||||||
// 配置模型
|
|
||||||
chatModel := openai.GPT3Dot5Turbo0613
|
|
||||||
if config.Conf.Ai.Model != "" {
|
|
||||||
chatModel = config.Conf.Ai.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认使用AI回复
|
|
||||||
conf := openai.DefaultConfig(config.Conf.Ai.ApiKey)
|
|
||||||
if config.Conf.Ai.BaseUrl != "" {
|
|
||||||
conf.BaseURL = fmt.Sprintf("%s/v1", config.Conf.Ai.BaseUrl)
|
|
||||||
}
|
|
||||||
client := openai.NewClientWithConfig(conf)
|
|
||||||
resp, err := client.CreateChatCompletion(
|
|
||||||
context.Background(),
|
|
||||||
openai.ChatCompletionRequest{
|
|
||||||
Model: chatModel,
|
|
||||||
Messages: messages,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("OpenAI聊天发起失败: %v", err.Error())
|
|
||||||
utils.SendMessage(m.FromUser, m.GroupUser, "AI炸啦~", 0)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送消息
|
|
||||||
utils.SendMessage(m.FromUser, m.GroupUser, "\n"+resp.Choices[0].Message.Content, 0)
|
|
||||||
}
|
|
||||||
45
initialization/plugin.go
Normal file
45
initialization/plugin.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package initialization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-wechat/common/current"
|
||||||
|
"go-wechat/model"
|
||||||
|
plugin "go-wechat/plugin"
|
||||||
|
"go-wechat/plugin/plugins"
|
||||||
|
"go-wechat/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Plugin
|
||||||
|
// @description: 初始化插件
|
||||||
|
func Plugin() {
|
||||||
|
// 定义一个处理器
|
||||||
|
dispatcher := plugin.NewMessageMatchDispatcher()
|
||||||
|
// 设置为异步处理
|
||||||
|
dispatcher.SetAsync(true)
|
||||||
|
|
||||||
|
// 注册插件
|
||||||
|
|
||||||
|
// 保存消息进数据库
|
||||||
|
dispatcher.RegisterHandler(func(*model.Message) bool {
|
||||||
|
return true
|
||||||
|
}, plugins.SaveToDb)
|
||||||
|
|
||||||
|
// 私聊指令消息
|
||||||
|
dispatcher.RegisterHandler(func(m *model.Message) bool {
|
||||||
|
// 私聊消息 或 群聊艾特机器人并且以/开头的消息
|
||||||
|
return (m.IsPrivateText() || (m.IsAt() && m.CleanContentStartWith("/"))) && service.CheckIsEnableCommand(m.FromUser)
|
||||||
|
}, plugins.Command)
|
||||||
|
|
||||||
|
// AI消息插件
|
||||||
|
dispatcher.RegisterHandler(func(m *model.Message) bool {
|
||||||
|
// 群内@或者私聊文字消息
|
||||||
|
return m.IsAt() || m.IsPrivateText()
|
||||||
|
}, plugins.AI)
|
||||||
|
|
||||||
|
// 欢迎新成员
|
||||||
|
dispatcher.RegisterHandler(func(m *model.Message) bool {
|
||||||
|
return m.IsNewUserJoin()
|
||||||
|
}, plugins.WelcomeNew)
|
||||||
|
|
||||||
|
// 注册消息处理器
|
||||||
|
current.SetRobotMessageHandler(plugin.DispatchMessage(dispatcher))
|
||||||
|
}
|
||||||
1
main.go
1
main.go
@@ -18,6 +18,7 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
initialization.InitConfig() // 初始化配置
|
initialization.InitConfig() // 初始化配置
|
||||||
initialization.InitWechatRobotInfo() // 初始化机器人信息
|
initialization.InitWechatRobotInfo() // 初始化机器人信息
|
||||||
|
initialization.Plugin() // 注册插件
|
||||||
tasks.InitTasks() // 初始化定时任务
|
tasks.InitTasks() // 初始化定时任务
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
model/leigod.go
Normal file
73
model/leigod.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
// LeiGodLoginResp
|
||||||
|
// @description: 雷神登录返回
|
||||||
|
type LeiGodLoginResp struct {
|
||||||
|
LoginInfo struct {
|
||||||
|
AccountToken string `json:"account_token"` // Token
|
||||||
|
ExpiryTime string `json:"expiry_time"` // 有效期
|
||||||
|
NnToken string `json:"nn_token"`
|
||||||
|
} `json:"login_info"`
|
||||||
|
UserInfo struct {
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
RegionCode int `json:"region_code"`
|
||||||
|
} `json:"user_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeiGodUserInfoResp
|
||||||
|
// @description: 雷神用户信息返回
|
||||||
|
type LeiGodUserInfoResp struct {
|
||||||
|
UserPauseTime int `json:"user_pause_time"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
CountryCode string `json:"country_code"`
|
||||||
|
Mobile string `json:"mobile"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
MasterAccount string `json:"master_account"`
|
||||||
|
Birthday string `json:"birthday"`
|
||||||
|
PublicIp string `json:"public_ip"`
|
||||||
|
Sex string `json:"sex"`
|
||||||
|
LastLoginTime string `json:"last_login_time"`
|
||||||
|
LastLoginIp string `json:"last_login_ip"`
|
||||||
|
PauseStatus string `json:"pause_status"` // 暂停状态
|
||||||
|
PauseStatusId int `json:"pause_status_id"` // 暂停状态 0未暂停1已暂停
|
||||||
|
LastPauseTime string `json:"last_pause_time"` // 最后一次暂停时间
|
||||||
|
VipLevel string `json:"vip_level"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
AvatarNew string `json:"avatar_new"`
|
||||||
|
PackageId string `json:"package_id"`
|
||||||
|
IsSwitchPackage int `json:"is_switch_package"`
|
||||||
|
PackageTitle string `json:"package_title"`
|
||||||
|
PackageLevel string `json:"package_level"`
|
||||||
|
BillingType string `json:"billing_type"`
|
||||||
|
Lang string `json:"lang"`
|
||||||
|
StopedRemaining string `json:"stoped_remaining"`
|
||||||
|
ExpiryTime string `json:"expiry_time"` // 剩余时长
|
||||||
|
ExpiryTimeSamp int `json:"expiry_time_samp"` // 剩余时长秒数
|
||||||
|
Address string `json:"address"`
|
||||||
|
MobileContactType string `json:"mobile_contact_type"`
|
||||||
|
MobileContactNumber string `json:"mobile_contact_number"`
|
||||||
|
MobileContactTitle string `json:"mobile_contact_title"`
|
||||||
|
RegionCode int `json:"region_code"`
|
||||||
|
IsPayUser string `json:"is_pay_user"`
|
||||||
|
WallLogSwitch string `json:"wall_log_switch"`
|
||||||
|
IsSetAdminPass int `json:"is_set_admin_pass"`
|
||||||
|
ExpiredExperienceTime string `json:"expired_experience_time"`
|
||||||
|
ExperienceExpiryTime string `json:"experience_expiry_time"`
|
||||||
|
ExperienceTime int `json:"experience_time"`
|
||||||
|
FirstInvoiceDiscount int `json:"first_invoice_discount"`
|
||||||
|
NnNumber string `json:"nn_number"`
|
||||||
|
UserSignature string `json:"user_signature"`
|
||||||
|
MobileExpiryTime string `json:"mobile_expiry_time"`
|
||||||
|
MobileExpiryTimeSamp int `json:"mobile_expiry_time_samp"`
|
||||||
|
MobilePauseStatus int `json:"mobile_pause_status"`
|
||||||
|
BlackExpiredTime string `json:"black_expired_time"`
|
||||||
|
MobileExperienceTime string `json:"mobile_experience_time"`
|
||||||
|
SuperTime string `json:"super_time"`
|
||||||
|
NowDate string `json:"now_date"`
|
||||||
|
NowTimeSamp int `json:"now_time_samp"`
|
||||||
|
UserEarnMinutes string `json:"user_earn_minutes"`
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"go-wechat/types"
|
"go-wechat/types"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ type Message struct {
|
|||||||
Signature string `json:"signature"`
|
Signature string `json:"signature"`
|
||||||
ToUser string `json:"toUser"`
|
ToUser string `json:"toUser"`
|
||||||
Type types.MessageType `json:"type"`
|
Type types.MessageType `json:"type"`
|
||||||
|
Raw string `json:"raw"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// systemMsgDataXml
|
// systemMsgDataXml
|
||||||
@@ -89,3 +91,37 @@ func (m Message) IsNewUserJoin() bool {
|
|||||||
func (m Message) IsAt() bool {
|
func (m Message) IsAt() bool {
|
||||||
return strings.HasSuffix(m.DisplayFullContent, "在群聊中@了你")
|
return strings.HasSuffix(m.DisplayFullContent, "在群聊中@了你")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPrivateText
|
||||||
|
// @description: 是否是私聊消息
|
||||||
|
// @receiver m
|
||||||
|
// @return bool
|
||||||
|
func (m Message) IsPrivateText() bool {
|
||||||
|
// 发信人不以@chatroom结尾且消息类型为文本
|
||||||
|
return !strings.HasSuffix(m.FromUser, "chatroom") && m.Type == types.MsgTypeText
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanContentStartWith
|
||||||
|
// @description: 判断是否包含指定消息前缀
|
||||||
|
// @receiver m
|
||||||
|
// @param prefix
|
||||||
|
// @return bool
|
||||||
|
func (m Message) CleanContentStartWith(prefix string) bool {
|
||||||
|
content := m.Content
|
||||||
|
|
||||||
|
// 如果是@消息,过滤掉@的内容
|
||||||
|
if m.IsAt() {
|
||||||
|
re := regexp.MustCompile(`@([^ | ]+)`)
|
||||||
|
matches := re.FindStringSubmatch(content)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
// 过滤掉第一个匹配到的
|
||||||
|
content = strings.Replace(content, matches[0], "", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去掉最前面的空格
|
||||||
|
content = strings.TrimLeft(content, " ")
|
||||||
|
content = strings.TrimLeft(content, " ")
|
||||||
|
|
||||||
|
return strings.HasPrefix(content, prefix)
|
||||||
|
}
|
||||||
|
|||||||
143
plugin/plugin.go
Normal file
143
plugin/plugin.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-wechat/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageHandler 消息处理函数
|
||||||
|
type MessageHandler func(msg *model.Message)
|
||||||
|
|
||||||
|
// MessageDispatcher 消息分发处理接口
|
||||||
|
// 跟 DispatchMessage 结合封装成 MessageHandler
|
||||||
|
type MessageDispatcher interface {
|
||||||
|
Dispatch(msg *model.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchMessage 跟 MessageDispatcher 结合封装成 MessageHandler
|
||||||
|
func DispatchMessage(dispatcher MessageDispatcher) func(msg *model.Message) {
|
||||||
|
return func(msg *model.Message) { dispatcher.Dispatch(msg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageDispatcher impl
|
||||||
|
|
||||||
|
// MessageContextHandler 消息处理函数
|
||||||
|
type MessageContextHandler func(ctx *MessageContext)
|
||||||
|
|
||||||
|
type MessageContextHandlerGroup []MessageContextHandler
|
||||||
|
|
||||||
|
// MessageContext 消息处理上下文对象
|
||||||
|
type MessageContext struct {
|
||||||
|
index int
|
||||||
|
abortIndex int
|
||||||
|
messageHandlers MessageContextHandlerGroup
|
||||||
|
*model.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next 主动调用下一个消息处理函数(或开始调用)
|
||||||
|
func (c *MessageContext) Next() {
|
||||||
|
c.index++
|
||||||
|
for c.index <= len(c.messageHandlers) {
|
||||||
|
if c.IsAbort() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handle := c.messageHandlers[c.index-1]
|
||||||
|
handle(c)
|
||||||
|
c.index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAbort 判断是否被中断
|
||||||
|
func (c *MessageContext) IsAbort() bool {
|
||||||
|
return c.abortIndex > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort 中断当前消息处理, 不会调用下一个消息处理函数, 但是不会中断当前的处理函数
|
||||||
|
func (c *MessageContext) Abort() {
|
||||||
|
c.abortIndex = c.index
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbortHandler 获取当前中断的消息处理函数
|
||||||
|
func (c *MessageContext) AbortHandler() MessageContextHandler {
|
||||||
|
if c.abortIndex > 0 {
|
||||||
|
return c.messageHandlers[c.abortIndex-1]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchFunc 消息匹配函数,返回为true则表示匹配
|
||||||
|
type MatchFunc func(*model.Message) bool
|
||||||
|
|
||||||
|
// MatchFuncList 将多个MatchFunc封装成一个MatchFunc
|
||||||
|
func MatchFuncList(matchFuncs ...MatchFunc) MatchFunc {
|
||||||
|
return func(message *model.Message) bool {
|
||||||
|
for _, matchFunc := range matchFuncs {
|
||||||
|
if !matchFunc(message) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type matchNode struct {
|
||||||
|
matchFunc MatchFunc
|
||||||
|
group MessageContextHandlerGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
type matchNodes []*matchNode
|
||||||
|
|
||||||
|
// MessageMatchDispatcher impl MessageDispatcher interface
|
||||||
|
//
|
||||||
|
// dispatcher := NewMessageMatchDispatcher()
|
||||||
|
// dispatcher.OnText(func(msg *model.Message){
|
||||||
|
// msg.ReplyText("hello")
|
||||||
|
// })
|
||||||
|
// bot := DefaultBot()
|
||||||
|
// bot.MessageHandler = DispatchMessage(dispatcher)
|
||||||
|
type MessageMatchDispatcher struct {
|
||||||
|
async bool
|
||||||
|
matchNodes matchNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMessageMatchDispatcher Constructor
|
||||||
|
func NewMessageMatchDispatcher() *MessageMatchDispatcher {
|
||||||
|
return &MessageMatchDispatcher{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAsync 设置是否异步处理
|
||||||
|
func (m *MessageMatchDispatcher) SetAsync(async bool) {
|
||||||
|
m.async = async
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch impl MessageDispatcher
|
||||||
|
// 遍历 MessageMatchDispatcher 所有的消息处理函数
|
||||||
|
// 获取所有匹配上的函数
|
||||||
|
// 执行处理的消息处理方法
|
||||||
|
func (m *MessageMatchDispatcher) Dispatch(msg *model.Message) {
|
||||||
|
var group MessageContextHandlerGroup
|
||||||
|
for _, node := range m.matchNodes {
|
||||||
|
if node.matchFunc(msg) {
|
||||||
|
group = append(group, node.group...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx := &MessageContext{Message: msg, messageHandlers: group}
|
||||||
|
if m.async {
|
||||||
|
go m.do(ctx)
|
||||||
|
} else {
|
||||||
|
m.do(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageMatchDispatcher) do(ctx *MessageContext) {
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterHandler 注册消息处理函数, 根据自己的需求自定义
|
||||||
|
// matchFunc返回true则表示处理对应的handlers
|
||||||
|
func (m *MessageMatchDispatcher) RegisterHandler(matchFunc MatchFunc, handlers ...MessageContextHandler) {
|
||||||
|
if matchFunc == nil {
|
||||||
|
panic("MatchFunc can not be nil")
|
||||||
|
}
|
||||||
|
node := &matchNode{matchFunc: matchFunc, group: handlers}
|
||||||
|
m.matchNodes = append(m.matchNodes, node)
|
||||||
|
}
|
||||||
172
plugin/plugins/ai.go
Normal file
172
plugin/plugins/ai.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
"github.com/sashabaranov/go-openai"
|
||||||
|
"go-wechat/client"
|
||||||
|
"go-wechat/common/current"
|
||||||
|
"go-wechat/config"
|
||||||
|
"go-wechat/entity"
|
||||||
|
"go-wechat/plugin"
|
||||||
|
"go-wechat/service"
|
||||||
|
"go-wechat/types"
|
||||||
|
"go-wechat/utils"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AI
|
||||||
|
// @description: AI消息
|
||||||
|
// @param m
|
||||||
|
func AI(m *plugin.MessageContext) {
|
||||||
|
if !config.Conf.Ai.Enable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取出所有启用了AI的好友或群组
|
||||||
|
var count int64
|
||||||
|
client.MySQL.Model(&entity.Friend{}).Where("enable_ai IS TRUE").Where("wxid = ?", m.FromUser).Count(&count)
|
||||||
|
if count < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预处理一下发送的消息,用正则去掉@机器人的内容
|
||||||
|
re := regexp.MustCompile(`@([^ | ]+)`)
|
||||||
|
matches := re.FindStringSubmatch(m.Content)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
// 过滤掉第一个匹配到的
|
||||||
|
m.Content = strings.Replace(m.Content, matches[0], "", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组装消息体
|
||||||
|
messages := make([]openai.ChatCompletionMessage, 0)
|
||||||
|
if config.Conf.Ai.Personality != "" {
|
||||||
|
// 填充人设
|
||||||
|
messages = append(messages, openai.ChatCompletionMessage{
|
||||||
|
Role: openai.ChatMessageRoleSystem,
|
||||||
|
Content: config.Conf.Ai.Personality,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询发信人前面几条文字信息,组装进来
|
||||||
|
var oldMessages []entity.Message
|
||||||
|
if m.GroupUser == "" {
|
||||||
|
// 私聊
|
||||||
|
oldMessages = getUserPrivateMessages(m.FromUser)
|
||||||
|
} else {
|
||||||
|
// 群聊
|
||||||
|
oldMessages = getGroupUserMessages(m.MsgId, m.FromUser, m.GroupUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翻转数组
|
||||||
|
slice.Reverse(oldMessages)
|
||||||
|
// 循环填充消息
|
||||||
|
for _, message := range oldMessages {
|
||||||
|
// 剔除@机器人的内容
|
||||||
|
msgStr := message.Content
|
||||||
|
matches = re.FindStringSubmatch(msgStr)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
// 过滤掉第一个匹配到的
|
||||||
|
msgStr = strings.Replace(msgStr, matches[0], "", 1)
|
||||||
|
}
|
||||||
|
// 填充消息
|
||||||
|
role := openai.ChatMessageRoleUser
|
||||||
|
if message.FromUser == current.GetRobotInfo().WxId {
|
||||||
|
// 如果收信人不是机器人,表示这条消息是 AI 发的
|
||||||
|
role = openai.ChatMessageRoleAssistant
|
||||||
|
}
|
||||||
|
messages = append(messages, openai.ChatCompletionMessage{
|
||||||
|
Role: role,
|
||||||
|
Content: msgStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填充用户消息
|
||||||
|
messages = append(messages, openai.ChatCompletionMessage{
|
||||||
|
Role: openai.ChatMessageRoleUser,
|
||||||
|
Content: m.Content,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 配置模型
|
||||||
|
chatModel := openai.GPT3Dot5Turbo0613
|
||||||
|
if config.Conf.Ai.Model != "" {
|
||||||
|
chatModel = config.Conf.Ai.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认使用AI回复
|
||||||
|
conf := openai.DefaultConfig(config.Conf.Ai.ApiKey)
|
||||||
|
if config.Conf.Ai.BaseUrl != "" {
|
||||||
|
conf.BaseURL = fmt.Sprintf("%s/v1", config.Conf.Ai.BaseUrl)
|
||||||
|
}
|
||||||
|
ai := openai.NewClientWithConfig(conf)
|
||||||
|
resp, err := ai.CreateChatCompletion(
|
||||||
|
context.Background(),
|
||||||
|
openai.ChatCompletionRequest{
|
||||||
|
Model: chatModel,
|
||||||
|
Messages: messages,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("OpenAI聊天发起失败: %v", err.Error())
|
||||||
|
utils.SendMessage(m.FromUser, m.GroupUser, "AI炸啦~", 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存一下AI 返回的消息,消息 Id 使用传入 Id 的负数
|
||||||
|
var replyMessage entity.Message
|
||||||
|
replyMessage.MsgId = -m.MsgId
|
||||||
|
replyMessage.CreateTime = int(time.Now().Local().Unix())
|
||||||
|
replyMessage.CreateAt = time.Now().Local()
|
||||||
|
replyMessage.Content = resp.Choices[0].Message.Content
|
||||||
|
replyMessage.FromUser = current.GetRobotInfo().WxId // 发信人是机器人
|
||||||
|
replyMessage.GroupUser = m.GroupUser // 群成员
|
||||||
|
replyMessage.ToUser = m.FromUser // 收信人是发信人
|
||||||
|
replyMessage.Type = types.MsgTypeText
|
||||||
|
service.SaveMessage(replyMessage) // 保存消息
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
replyMsg := resp.Choices[0].Message.Content
|
||||||
|
if m.GroupUser != "" {
|
||||||
|
replyMsg = "\n" + resp.Choices[0].Message.Content
|
||||||
|
}
|
||||||
|
utils.SendMessage(m.FromUser, m.GroupUser, replyMsg, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGroupUserMessages
|
||||||
|
// @description: 获取群成员消息
|
||||||
|
// @return records
|
||||||
|
func getGroupUserMessages(msgId int64, groupId, groupUserId string) (records []entity.Message) {
|
||||||
|
subQuery := client.MySQL.
|
||||||
|
Where("from_user = ? AND group_user = ? AND display_full_content LIKE ?", groupId, groupUserId, "%在群聊中@了你").
|
||||||
|
Or("to_user = ? AND group_user = ?", groupId, groupUserId)
|
||||||
|
|
||||||
|
client.MySQL.Model(&entity.Message{}).
|
||||||
|
Where("msg_id != ?", msgId).
|
||||||
|
Where("type = ?", types.MsgTypeText).
|
||||||
|
Where("create_at >= DATE_SUB(NOW(),INTERVAL 30 MINUTE)").
|
||||||
|
Where(subQuery).
|
||||||
|
Order("create_at desc").
|
||||||
|
Limit(4).Find(&records)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUserPrivateMessages
|
||||||
|
// @description: 获取用户私聊消息
|
||||||
|
// @return records
|
||||||
|
func getUserPrivateMessages(userId string) (records []entity.Message) {
|
||||||
|
subQuery := client.MySQL.
|
||||||
|
Where("from_user = ?", userId).Or("to_user = ?", userId)
|
||||||
|
|
||||||
|
client.MySQL.Model(&entity.Message{}).
|
||||||
|
Where("type = ?", types.MsgTypeText).
|
||||||
|
Where("create_at >= DATE_SUB(NOW(),INTERVAL 30 MINUTE)").
|
||||||
|
Where(subQuery).
|
||||||
|
Order("create_at desc").
|
||||||
|
Limit(4).Find(&records)
|
||||||
|
return
|
||||||
|
}
|
||||||
51
plugin/plugins/command.go
Normal file
51
plugin/plugins/command.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-wechat/plugin"
|
||||||
|
"go-wechat/plugin/plugins/command"
|
||||||
|
"go-wechat/utils"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command
|
||||||
|
// @description: 自定义指令
|
||||||
|
// @param m
|
||||||
|
func Command(m *plugin.MessageContext) {
|
||||||
|
// 如果是群聊,提取出消息
|
||||||
|
content := m.Content
|
||||||
|
|
||||||
|
if m.IsGroup() {
|
||||||
|
re := regexp.MustCompile(`@([^ | ]+)`)
|
||||||
|
matches := re.FindStringSubmatch(content)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
// 过滤掉第一个匹配到的
|
||||||
|
content = strings.Replace(content, matches[0], "", 1)
|
||||||
|
}
|
||||||
|
// 去掉最前面的空格
|
||||||
|
content = strings.TrimLeft(content, " ")
|
||||||
|
content = strings.TrimLeft(content, " ")
|
||||||
|
}
|
||||||
|
// 判断是不是指令
|
||||||
|
if !strings.HasPrefix(content, "/") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用空格分割消息,下标0表示指令
|
||||||
|
msgArray := strings.Split(content, " ")
|
||||||
|
cmd := msgArray[0]
|
||||||
|
|
||||||
|
switch cmd {
|
||||||
|
case "/帮助", "/h", "/help", "/?", "/?":
|
||||||
|
command.HelpCmd(m)
|
||||||
|
case "/雷神", "/ls":
|
||||||
|
command.LeiGodCmd(m.FromUser, msgArray[1], msgArray[2:]...)
|
||||||
|
case "/肯德基", "/kfc":
|
||||||
|
command.KfcCrazyThursdayCmd(m.FromUser)
|
||||||
|
default:
|
||||||
|
utils.SendMessage(m.FromUser, m.GroupUser, "指令错误", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中止后续消息处理
|
||||||
|
m.Abort()
|
||||||
|
}
|
||||||
29
plugin/plugins/command/help.go
Normal file
29
plugin/plugins/command/help.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-wechat/plugin"
|
||||||
|
"go-wechat/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HelpCmd
|
||||||
|
// @description: 帮助指令
|
||||||
|
func HelpCmd(m *plugin.MessageContext) {
|
||||||
|
str := `帮助菜单:
|
||||||
|
指令消息必须以'/'开头,比如: '/帮助'。
|
||||||
|
支持的指令:
|
||||||
|
|
||||||
|
#1. 雷神加速器
|
||||||
|
/ls option args
|
||||||
|
option: 指令选项,可选值:
|
||||||
|
绑定账户:'绑定'、'b',参数: 账户名 密码 [-f],-f表示强制绑定,非必传项
|
||||||
|
详情: '详情'、'i'
|
||||||
|
暂停: '暂停'、'p'
|
||||||
|
示例: 绑定:
|
||||||
|
/ls 绑定 123456 123456 或者 /ls b 123456 123456
|
||||||
|
|
||||||
|
#2. 肯德基疯狂星期四文案
|
||||||
|
/kfc、/肯德基
|
||||||
|
`
|
||||||
|
utils.SendMessage(m.FromUser, m.GroupUser, str, 0)
|
||||||
|
|
||||||
|
}
|
||||||
95
plugin/plugins/command/kfc.go
Normal file
95
plugin/plugins/command/kfc.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go-wechat/utils"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KfcCrazyThursdayCmd
|
||||||
|
// @description: 肯德基疯狂星期四文案
|
||||||
|
// @param userId string 发信人
|
||||||
|
func KfcCrazyThursdayCmd(userId string) {
|
||||||
|
// 随机选一个接口调用
|
||||||
|
str := kfcApi1()
|
||||||
|
if str == "" {
|
||||||
|
str = kfcApi2()
|
||||||
|
}
|
||||||
|
if str == "" {
|
||||||
|
str = kfcApi3()
|
||||||
|
}
|
||||||
|
if str == "" {
|
||||||
|
str = "文案获取失败"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
utils.SendMessage(userId, "", str, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// kfcApi1
|
||||||
|
// @description: 肯德基疯狂星期四文案接口1
|
||||||
|
// @return string
|
||||||
|
func kfcApi1() string {
|
||||||
|
res := resty.New()
|
||||||
|
resp, err := res.R().
|
||||||
|
Post("https://api.jixs.cc/api/wenan-fkxqs/index.php")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("KFC接口1文案获取失败: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("KFC接口1文案获取结果: %s", resp.String())
|
||||||
|
return resp.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// kfcApi2
|
||||||
|
// @description: 肯德基疯狂星期四文案接口2
|
||||||
|
// @return string
|
||||||
|
func kfcApi2() string {
|
||||||
|
type result struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Data struct {
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var resData result
|
||||||
|
|
||||||
|
res := resty.New()
|
||||||
|
resp, err := res.R().
|
||||||
|
SetResult(&resData).
|
||||||
|
Post("https://api.jixs.cc/api/wenan-fkxqs/index.php")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("KFC接口2文案获取失败: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("KFC接口2文案获取结果: %s", resp.String())
|
||||||
|
if resData.Data.Msg != "" {
|
||||||
|
return resData.Data.Msg
|
||||||
|
}
|
||||||
|
return resp.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// kfcApi3
|
||||||
|
// @description: 肯德基疯狂星期四文案接口3
|
||||||
|
// @return string
|
||||||
|
func kfcApi3() string {
|
||||||
|
type result struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var resData result
|
||||||
|
|
||||||
|
res := resty.New()
|
||||||
|
resp, err := res.R().
|
||||||
|
SetResult(&resData).
|
||||||
|
Post("https://api.pearktrue.cn/api/kfc")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("KFC接口3文案获取失败: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("KFC接口3文案获取结果: %s", resp.String())
|
||||||
|
if resData.Text != "" {
|
||||||
|
return resData.Text
|
||||||
|
}
|
||||||
|
return resp.String()
|
||||||
|
}
|
||||||
205
plugin/plugins/command/leigod.go
Normal file
205
plugin/plugins/command/leigod.go
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go-wechat/client"
|
||||||
|
"go-wechat/entity"
|
||||||
|
"go-wechat/model"
|
||||||
|
"go-wechat/utils"
|
||||||
|
"go-wechat/vo"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// leiGod
|
||||||
|
// @description: 雷神加速器相关接口
|
||||||
|
type leiGodI interface {
|
||||||
|
binding(string, string, bool) string // 绑定雷神加速器账号
|
||||||
|
info() string // 账户详情
|
||||||
|
pause() string // 暂停加速
|
||||||
|
}
|
||||||
|
|
||||||
|
type leiGod struct {
|
||||||
|
userId string // 用户Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLeiGod
|
||||||
|
// @description: 创建一个雷神加速器实例
|
||||||
|
// @param userId
|
||||||
|
// @return leiGodI
|
||||||
|
func newLeiGod(userId string) leiGodI {
|
||||||
|
return &leiGod{userId: userId}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeiGodCmd
|
||||||
|
// @description: 雷神加速器指令
|
||||||
|
// @param userId
|
||||||
|
// @param cmd
|
||||||
|
// @param args
|
||||||
|
// @return string
|
||||||
|
func LeiGodCmd(userId, cmd string, args ...string) {
|
||||||
|
lg := newLeiGod(userId)
|
||||||
|
|
||||||
|
var replyMsg string
|
||||||
|
switch cmd {
|
||||||
|
case "绑定", "b":
|
||||||
|
var force bool
|
||||||
|
if len(args) == 3 && args[2] == "-f" {
|
||||||
|
force = true
|
||||||
|
}
|
||||||
|
replyMsg = lg.binding(args[0], args[1], force)
|
||||||
|
case "详情", "i":
|
||||||
|
replyMsg = lg.info()
|
||||||
|
case "暂停", "p":
|
||||||
|
replyMsg = lg.pause()
|
||||||
|
default:
|
||||||
|
replyMsg = "指令错误"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回消息
|
||||||
|
if strings.TrimSpace(replyMsg) != "" {
|
||||||
|
utils.SendMessage(userId, "", replyMsg, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// binding
|
||||||
|
// @description: 绑定雷神加速器账号
|
||||||
|
// @receiver l
|
||||||
|
// @param account
|
||||||
|
// @param password
|
||||||
|
// @param force
|
||||||
|
// @return flag
|
||||||
|
func (l leiGod) binding(account, password string, force bool) (replyMsg string) {
|
||||||
|
log.Printf("用户[%s]绑定雷神加速器账号[%s] -> %s", l.userId, account, password)
|
||||||
|
|
||||||
|
// 取出已绑定的账号
|
||||||
|
var data entity.PluginData
|
||||||
|
client.MySQL.Where("user_id = ?", l.userId).Where("plugin_code = 'leigod'").First(&data)
|
||||||
|
|
||||||
|
var ac vo.LeiGodAccount
|
||||||
|
if data.UserId != "" {
|
||||||
|
if err := json.Unmarshal([]byte(data.Data), &ac); err != nil {
|
||||||
|
log.Printf("用户[%s]已绑定雷神账号解析失败: %v", l.userId, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("用户[%s]已绑定账号[%s]", l.userId, ac.Account)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果已经绑定账号,且不是强制绑定,则返回
|
||||||
|
if ac.Account != "" && !force {
|
||||||
|
replyMsg = "您已绑定账号[" + ac.Account + "],如需更换请使用 -f 参数: \n/雷神 绑定 账号 密码 -f"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accountStr := fmt.Sprintf("{\"account\": \"%s\", \"password\":\"%s\"}", account, password)
|
||||||
|
|
||||||
|
// 绑定账号
|
||||||
|
var err error
|
||||||
|
if data.UserId != "" {
|
||||||
|
// 修改
|
||||||
|
err = client.MySQL.Model(&data).
|
||||||
|
Where("user_id = ?", l.userId).
|
||||||
|
Where("plugin_code = 'leigod'").
|
||||||
|
Update("data", accountStr).Error
|
||||||
|
} else {
|
||||||
|
// 新增
|
||||||
|
data = entity.PluginData{
|
||||||
|
UserId: l.userId,
|
||||||
|
PluginCode: "leigod",
|
||||||
|
Data: accountStr,
|
||||||
|
}
|
||||||
|
err = client.MySQL.Create(&data).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("用户[%s]绑定雷神账号失败: %v", l.userId, err)
|
||||||
|
replyMsg = "绑定失败: " + err.Error()
|
||||||
|
} else {
|
||||||
|
replyMsg = "绑定成功"
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// info
|
||||||
|
// @description: 账户详情
|
||||||
|
// @receiver l
|
||||||
|
// @return replyMsg
|
||||||
|
func (l leiGod) info() (replyMsg string) {
|
||||||
|
log.Printf("用户[%s]获取雷神账户详情", l.userId)
|
||||||
|
|
||||||
|
// 取出已绑定的账号
|
||||||
|
var data entity.PluginData
|
||||||
|
err := client.MySQL.Where("user_id = ?", l.userId).Where("plugin_code = 'leigod'").First(&data).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("用户[%s]获取雷神账户详情失败: %v", l.userId, err)
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
replyMsg = "您还未绑定账号,请先绑定后再使用,绑定指定:\n/雷神 绑定 你的账号 你的密码"
|
||||||
|
} else {
|
||||||
|
replyMsg = "系统错误: " + err.Error()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析为结构体
|
||||||
|
var ac vo.LeiGodAccount
|
||||||
|
if err = json.Unmarshal([]byte(data.Data), &ac); err != nil {
|
||||||
|
log.Printf("用户[%s]已绑定雷神账号解析失败: %v", l.userId, err)
|
||||||
|
replyMsg = "系统炸了,请耐心等待修复"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lgu := utils.LeiGodUtil(ac.Account, ac.Password)
|
||||||
|
if err = lgu.Login(); err != nil {
|
||||||
|
return "登录失败: " + err.Error()
|
||||||
|
}
|
||||||
|
var ui model.LeiGodUserInfoResp
|
||||||
|
if ui, err = lgu.Info(); err != nil {
|
||||||
|
return "获取详情失败: " + err.Error()
|
||||||
|
}
|
||||||
|
replyMsg = fmt.Sprintf("#账户 %s\n#剩余时长 %s\n#暂停状态 %s\n#最后暂停时间 %s",
|
||||||
|
ui.Mobile, ui.ExpiryTime, ui.PauseStatus, ui.LastPauseTime)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// pause
|
||||||
|
// @description: 暂停加速
|
||||||
|
// @receiver l
|
||||||
|
// @return flag
|
||||||
|
func (l leiGod) pause() (replyMsg string) {
|
||||||
|
log.Printf("用户[%s]暂停加速", l.userId)
|
||||||
|
|
||||||
|
// 取出已绑定的账号
|
||||||
|
var data entity.PluginData
|
||||||
|
err := client.MySQL.Where("user_id = ?", l.userId).Where("plugin_code = 'leigod'").First(&data).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("用户[%s]获取雷神账户详情失败: %v", l.userId, err)
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
replyMsg = "您还未绑定账号,请先绑定后再使用,绑定指定:\n/雷神 绑定 你的账号 你的密码"
|
||||||
|
} else {
|
||||||
|
replyMsg = "系统错误: " + err.Error()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析为结构体
|
||||||
|
var ac vo.LeiGodAccount
|
||||||
|
if err = json.Unmarshal([]byte(data.Data), &ac); err != nil {
|
||||||
|
log.Printf("用户[%s]已绑定雷神账号解析失败: %v", l.userId, err)
|
||||||
|
replyMsg = "系统炸了,请耐心等待修复"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lgu := utils.LeiGodUtil(ac.Account, ac.Password)
|
||||||
|
if err = lgu.Login(); err != nil {
|
||||||
|
return "登录失败: " + err.Error()
|
||||||
|
}
|
||||||
|
if err = lgu.Pause(); err != nil {
|
||||||
|
return "暂停失败: " + err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "暂停成功"
|
||||||
|
}
|
||||||
27
plugin/plugins/save2db.go
Normal file
27
plugin/plugins/save2db.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-wechat/entity"
|
||||||
|
"go-wechat/plugin"
|
||||||
|
"go-wechat/service"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SaveToDb
|
||||||
|
// @description: 保存消息到数据库
|
||||||
|
// @param m
|
||||||
|
func SaveToDb(m *plugin.MessageContext) {
|
||||||
|
var ent entity.Message
|
||||||
|
ent.MsgId = m.MsgId
|
||||||
|
ent.CreateTime = m.CreateTime
|
||||||
|
ent.CreateAt = time.Unix(int64(m.CreateTime), 0)
|
||||||
|
ent.Content = m.Content
|
||||||
|
ent.FromUser = m.FromUser
|
||||||
|
ent.GroupUser = m.GroupUser
|
||||||
|
ent.ToUser = m.ToUser
|
||||||
|
ent.Type = m.Type
|
||||||
|
ent.DisplayFullContent = m.DisplayFullContent
|
||||||
|
ent.Raw = m.Raw
|
||||||
|
// 保存入库
|
||||||
|
service.SaveMessage(ent)
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
package handler
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go-wechat/client"
|
"go-wechat/client"
|
||||||
"go-wechat/config"
|
"go-wechat/config"
|
||||||
"go-wechat/entity"
|
"go-wechat/entity"
|
||||||
"go-wechat/model"
|
"go-wechat/plugin"
|
||||||
"go-wechat/utils"
|
"go-wechat/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleNewUserJoin
|
// WelcomeNew
|
||||||
// @description: 欢迎新成员
|
// @description: 欢迎新成员
|
||||||
// @param m
|
// @param m
|
||||||
func handleNewUserJoin(m model.Message) {
|
func WelcomeNew(m *plugin.MessageContext) {
|
||||||
// 判断是否开启迎新
|
// 判断是否开启迎新
|
||||||
var count int64
|
var count int64
|
||||||
client.MySQL.Model(&entity.Friend{}).Where("enable_welcome IS TRUE").Where("wxid = ?", m.FromUser).Count(&count)
|
client.MySQL.Model(&entity.Friend{}).Where("enable_welcome IS TRUE").Where("wxid = ?", m.FromUser).Count(&count)
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package plugins
|
|
||||||
|
|
||||||
// Message
|
|
||||||
// @description: 插件消息
|
|
||||||
type Message struct {
|
|
||||||
GroupId string // 消息来源群Id
|
|
||||||
UserId string // 消息来源用户Id
|
|
||||||
Message string // 消息内容
|
|
||||||
IsBreak bool // 是否中断消息传递
|
|
||||||
}
|
|
||||||
@@ -16,8 +16,8 @@ wechat:
|
|||||||
host: wechat:19088
|
host: wechat:19088
|
||||||
# 是否在启动的时候自动设置hook服务的回调
|
# 是否在启动的时候自动设置hook服务的回调
|
||||||
autoSetCallback: true
|
autoSetCallback: true
|
||||||
# 回调IP,如果是Docker运行,本参数必填,如果Docker修改了映射,格式为 ip:port,如果使用项目提供的docker-compsoe.yaml文件启动,可以不写
|
# 回调IP,如果是Docker运行,本参数必填,如果Docker修改了映射,格式为 ip:port,如果使用项目提供的docker-compsoe.yaml文件启动,可以填`auto`
|
||||||
callback:
|
callback: auto
|
||||||
|
|
||||||
# 数据库
|
# 数据库
|
||||||
mysql:
|
mysql:
|
||||||
|
|||||||
@@ -50,3 +50,13 @@ func GetAllEnableChatRank() (records []entity.Friend, err error) {
|
|||||||
err = client.MySQL.Where("enable_chat_rank = ?", 1).Where("wxid LIKE '%@chatroom'").Find(&records).Error
|
err = client.MySQL.Where("enable_chat_rank = ?", 1).Where("wxid LIKE '%@chatroom'").Find(&records).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIsEnableCommand
|
||||||
|
// @description: 检查用户是否启用了指令
|
||||||
|
// @param userId
|
||||||
|
// @return flag
|
||||||
|
func CheckIsEnableCommand(userId string) (flag bool) {
|
||||||
|
var coo int64
|
||||||
|
client.MySQL.Model(&entity.Friend{}).Where("enable_command = 1").Where("wxid = ?", userId).Count(&coo)
|
||||||
|
return coo > 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,18 @@ import (
|
|||||||
"go-wechat/client"
|
"go-wechat/client"
|
||||||
"go-wechat/entity"
|
"go-wechat/entity"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SaveMessage
|
// SaveMessage
|
||||||
// @description: 消息入库
|
// @description: 消息入库
|
||||||
// @param msg
|
// @param msg
|
||||||
func SaveMessage(msg entity.Message) {
|
func SaveMessage(msg entity.Message) {
|
||||||
|
if flag, _ := strconv.ParseBool(os.Getenv("DONT_SAVE")); flag {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 检查消息是否存在,存在就跳过
|
// 检查消息是否存在,存在就跳过
|
||||||
var count int64
|
var count int64
|
||||||
err := client.MySQL.Model(&entity.Message{}).Where("msg_id = ?", msg.MsgId).Count(&count).Error
|
err := client.MySQL.Model(&entity.Message{}).Where("msg_id = ?", msg.MsgId).Count(&count).Error
|
||||||
@@ -18,6 +24,7 @@ func SaveMessage(msg entity.Message) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
|
//log.Printf("消息已存在,消息Id: %d", msg.MsgId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = client.MySQL.Create(&msg).Error
|
err = client.MySQL.Create(&msg).Error
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package watergroup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go-wechat/config"
|
||||||
"go-wechat/service"
|
"go-wechat/service"
|
||||||
"go-wechat/utils"
|
"go-wechat/utils"
|
||||||
"log"
|
"log"
|
||||||
@@ -21,11 +22,17 @@ func Month() {
|
|||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
// 消息统计
|
// 消息统计
|
||||||
dealMonth(group.Wxid)
|
dealMonth(group.Wxid)
|
||||||
|
|
||||||
|
res, ok := config.Conf.Resource["wordcloud"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 获取上个月月份
|
// 获取上个月月份
|
||||||
yd := time.Now().Local().AddDate(0, 0, -1).Format("200601")
|
yd := time.Now().Local().AddDate(0, 0, -1).Format("200601")
|
||||||
// 发送词云
|
// 发送词云
|
||||||
fileName := fmt.Sprintf("%s_%s.png", yd, group.Wxid)
|
fileName := fmt.Sprintf("%s_%s.png", yd, group.Wxid)
|
||||||
utils.SendImage(group.Wxid, "D:\\Share\\wordcloud\\"+fileName, 0)
|
utils.SendImage(group.Wxid, fmt.Sprintf(res.Path, fileName), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package watergroup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go-wechat/config"
|
||||||
"go-wechat/service"
|
"go-wechat/service"
|
||||||
"go-wechat/utils"
|
"go-wechat/utils"
|
||||||
"log"
|
"log"
|
||||||
@@ -21,11 +22,17 @@ func Week() {
|
|||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
// 消息统计
|
// 消息统计
|
||||||
dealWeek(group.Wxid)
|
dealWeek(group.Wxid)
|
||||||
|
|
||||||
|
res, ok := config.Conf.Resource["wordcloud"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 获取上周周数
|
// 获取上周周数
|
||||||
year, weekNo := time.Now().Local().AddDate(0, 0, -1).ISOWeek()
|
year, weekNo := time.Now().Local().AddDate(0, 0, -1).ISOWeek()
|
||||||
// 发送词云
|
// 发送词云
|
||||||
fileName := fmt.Sprintf("%d%d_%s.png", year, weekNo, group.Wxid)
|
fileName := fmt.Sprintf("%d%d_%s.png", year, weekNo, group.Wxid)
|
||||||
utils.SendImage(group.Wxid, "D:\\Share\\wordcloud\\"+fileName, 0)
|
utils.SendImage(group.Wxid, fmt.Sprintf(res.Path, fileName), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package watergroup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go-wechat/config"
|
||||||
"go-wechat/service"
|
"go-wechat/service"
|
||||||
"go-wechat/utils"
|
"go-wechat/utils"
|
||||||
"log"
|
"log"
|
||||||
@@ -23,11 +24,17 @@ func Yesterday() {
|
|||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
// 消息统计
|
// 消息统计
|
||||||
dealYesterday(group.Wxid)
|
dealYesterday(group.Wxid)
|
||||||
|
|
||||||
|
res, ok := config.Conf.Resource["wordcloud"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 获取昨日日期
|
// 获取昨日日期
|
||||||
yd := time.Now().Local().AddDate(0, 0, -1).Format("20060102")
|
yd := time.Now().Local().AddDate(0, 0, -1).Format("20060102")
|
||||||
// 发送词云
|
// 发送词云
|
||||||
fileName := fmt.Sprintf("%s_%s.png", yd, group.Wxid)
|
fileName := fmt.Sprintf("%s_%s.png", yd, group.Wxid)
|
||||||
utils.SendImage(group.Wxid, "D:\\Share\\wordcloud\\"+fileName, 0)
|
utils.SendImage(group.Wxid, fmt.Sprintf(res.Path, fileName), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package tcpserver
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"go-wechat/config"
|
"go-wechat/config"
|
||||||
"go-wechat/handler"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@@ -24,7 +23,7 @@ func process(conn net.Conn) {
|
|||||||
log.Printf("[%s]返回数据失败,错误信息: %v", conn.RemoteAddr(), err)
|
log.Printf("[%s]返回数据失败,错误信息: %v", conn.RemoteAddr(), err)
|
||||||
}
|
}
|
||||||
log.Printf("[%s]数据长度: %d", conn.RemoteAddr(), buf.Len())
|
log.Printf("[%s]数据长度: %d", conn.RemoteAddr(), buf.Len())
|
||||||
go handler.Parse(conn.RemoteAddr(), buf.Bytes())
|
go parse(conn.RemoteAddr(), buf.Bytes())
|
||||||
|
|
||||||
// 转发到其他地方去
|
// 转发到其他地方去
|
||||||
if len(config.Conf.Wechat.Forward) > 0 {
|
if len(config.Conf.Wechat.Forward) > 0 {
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
package handler
|
package tcpserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"go-wechat/entity"
|
"go-wechat/common/current"
|
||||||
"go-wechat/model"
|
"go-wechat/model"
|
||||||
"go-wechat/service"
|
|
||||||
"go-wechat/types"
|
"go-wechat/types"
|
||||||
"go-wechat/utils"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse
|
// parse
|
||||||
// @description: 解析消息
|
// @description: 解析消息
|
||||||
// @param msg
|
// @param msg
|
||||||
func Parse(remoteAddr net.Addr, msg []byte) {
|
func parse(remoteAddr net.Addr, msg []byte) {
|
||||||
var m model.Message
|
var m model.Message
|
||||||
if err := json.Unmarshal(msg, &m); err != nil {
|
if err := json.Unmarshal(msg, &m); err != nil {
|
||||||
log.Printf("[%s]消息解析失败: %v", remoteAddr, err)
|
log.Printf("[%s]消息解析失败: %v", remoteAddr, err)
|
||||||
log.Printf("[%s]消息内容: %d -> %v", remoteAddr, len(msg), string(msg))
|
log.Printf("[%s]消息内容: %d -> %v", remoteAddr, len(msg), string(msg))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 记录原始数据
|
||||||
|
m.Raw = string(msg)
|
||||||
|
|
||||||
// 提取出群成员信息
|
// 提取出群成员信息
|
||||||
// Sys类型的消息正文不包含微信 Id,所以不需要处理
|
// Sys类型的消息正文不包含微信 Id,所以不需要处理
|
||||||
if m.IsGroup() && m.Type != types.MsgTypeSys {
|
if m.IsGroup() && m.Type != types.MsgTypeSys {
|
||||||
@@ -38,33 +38,9 @@ func Parse(remoteAddr net.Addr, msg []byte) {
|
|||||||
}
|
}
|
||||||
log.Printf("%s\n消息来源: %s\n群成员: %s\n消息类型: %v\n消息内容: %s", remoteAddr, m.FromUser, m.GroupUser, m.Type, m.Content)
|
log.Printf("%s\n消息来源: %s\n群成员: %s\n消息类型: %v\n消息内容: %s", remoteAddr, m.FromUser, m.GroupUser, m.Type, m.Content)
|
||||||
|
|
||||||
// 异步处理消息
|
// 插件不为空,开始执行
|
||||||
go func() {
|
if p := current.GetRobotMessageHandler(); p != nil {
|
||||||
if m.IsNewUserJoin() {
|
p(&m)
|
||||||
log.Printf("%s -> 开始迎新 -> %s", m.FromUser, m.Content)
|
}
|
||||||
// 欢迎新成员
|
|
||||||
go handleNewUserJoin(m)
|
|
||||||
} else if m.IsAt() {
|
|
||||||
// @机器人的消息
|
|
||||||
go handleAtMessage(m)
|
|
||||||
} else if !strings.Contains(m.FromUser, "@") && m.Type == types.MsgTypeText {
|
|
||||||
// 私聊消息处理
|
|
||||||
utils.SendMessage(m.FromUser, "", "暂未开启私聊AI", 0)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 转换为结构体之后入库
|
|
||||||
var ent entity.Message
|
|
||||||
ent.MsgId = m.MsgId
|
|
||||||
ent.CreateTime = m.CreateTime
|
|
||||||
ent.CreateAt = time.Unix(int64(m.CreateTime), 0)
|
|
||||||
ent.Content = m.Content
|
|
||||||
ent.FromUser = m.FromUser
|
|
||||||
ent.GroupUser = m.GroupUser
|
|
||||||
ent.ToUser = m.ToUser
|
|
||||||
ent.Type = m.Type
|
|
||||||
ent.DisplayFullContent = m.DisplayFullContent
|
|
||||||
ent.Raw = string(msg)
|
|
||||||
|
|
||||||
go service.SaveMessage(ent)
|
|
||||||
}
|
}
|
||||||
@@ -29,9 +29,13 @@ func ClearCallback() {
|
|||||||
// @param host
|
// @param host
|
||||||
func SetCallback(userHost string) {
|
func SetCallback(userHost string) {
|
||||||
// 获取本机IP地址
|
// 获取本机IP地址
|
||||||
host := net.ParseIP(netutil.GetInternalIp()).String()
|
host := userHost
|
||||||
|
if userHost == "auto" {
|
||||||
|
host = net.ParseIP(netutil.GetInternalIp()).String()
|
||||||
|
}
|
||||||
|
|
||||||
port := 19099
|
port := 19099
|
||||||
if userHost != "" {
|
if userHost != "" && userHost != "auto" {
|
||||||
uh := strings.Split(strings.TrimSpace(userHost), ":")
|
uh := strings.Split(strings.TrimSpace(userHost), ":")
|
||||||
host = uh[0]
|
host = uh[0]
|
||||||
if len(uh) == 2 {
|
if len(uh) == 2 {
|
||||||
|
|||||||
169
utils/leigod.go
Normal file
169
utils/leigod.go
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go-wechat/model"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LeiGod
|
||||||
|
// @description: 雷神加速器相关接口
|
||||||
|
type LeiGod interface {
|
||||||
|
Login() error // 登录
|
||||||
|
Info() (model.LeiGodUserInfoResp, error) // 获取用户信息
|
||||||
|
Pause() error // 暂停加速
|
||||||
|
}
|
||||||
|
|
||||||
|
type leiGod struct {
|
||||||
|
account, password string // 账号、密码
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeiGodUtil
|
||||||
|
// @description: 创建一个雷神加速器工具类
|
||||||
|
// @param userId
|
||||||
|
// @return leiGodI
|
||||||
|
func LeiGodUtil(account, password string) LeiGod {
|
||||||
|
// 把密码md5一下
|
||||||
|
hash := md5.New()
|
||||||
|
hash.Write([]byte(password))
|
||||||
|
password = fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
|
||||||
|
return &leiGod{account: account, password: password}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login
|
||||||
|
// @description: 登录
|
||||||
|
// @receiver l
|
||||||
|
// @return string
|
||||||
|
func (l *leiGod) Login() (err error) {
|
||||||
|
// 组装参数
|
||||||
|
param := map[string]any{
|
||||||
|
"account_token": nil,
|
||||||
|
"country_code": 86,
|
||||||
|
"lang": "zh_CN",
|
||||||
|
"os_type": 4,
|
||||||
|
"mobile_num": l.account,
|
||||||
|
"username": l.account,
|
||||||
|
"password": l.password,
|
||||||
|
"region_code": 1,
|
||||||
|
"src_channel": "guanwang",
|
||||||
|
"sem_ad_img_url": map[string]any{
|
||||||
|
"btn_yrl": "",
|
||||||
|
"url": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pbs, _ := json.Marshal(param)
|
||||||
|
|
||||||
|
var loginResp model.Response[any]
|
||||||
|
var resp *resty.Response
|
||||||
|
|
||||||
|
res := resty.New()
|
||||||
|
resp, err = res.R().
|
||||||
|
SetHeader("Content-Type", "application/json;chartset=utf-8").
|
||||||
|
SetBody(string(pbs)).
|
||||||
|
SetResult(&loginResp).
|
||||||
|
Post("https://webapi.leigod.com/api/auth/login")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("雷神加速器登录失败: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("雷神加速器登录结果: %s", unicodeToText(resp.String()))
|
||||||
|
|
||||||
|
// 返回状态码不是0表示有错
|
||||||
|
if loginResp.Code != 0 {
|
||||||
|
return errors.New(loginResp.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将Data字段转为结构体
|
||||||
|
var bs []byte
|
||||||
|
if bs, err = json.Marshal(loginResp.Data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginInfo model.LeiGodLoginResp
|
||||||
|
if err = json.Unmarshal(bs, &loginInfo); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginInfo.LoginInfo.AccountToken != "" {
|
||||||
|
l.token = loginInfo.LoginInfo.AccountToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info
|
||||||
|
// @description: 获取用户信息
|
||||||
|
// @receiver l
|
||||||
|
// @return string
|
||||||
|
func (l *leiGod) Info() (ui model.LeiGodUserInfoResp, err error) {
|
||||||
|
// 组装参数
|
||||||
|
param := map[string]any{
|
||||||
|
"account_token": l.token,
|
||||||
|
"lang": "zh_CN",
|
||||||
|
"os_type": 4,
|
||||||
|
}
|
||||||
|
pbs, _ := json.Marshal(param)
|
||||||
|
|
||||||
|
var userInfoResp model.Response[model.LeiGodUserInfoResp]
|
||||||
|
var resp *resty.Response
|
||||||
|
|
||||||
|
res := resty.New()
|
||||||
|
resp, err = res.R().
|
||||||
|
SetHeader("Content-Type", "application/json;chartset=utf-8").
|
||||||
|
SetBody(string(pbs)).
|
||||||
|
SetResult(&userInfoResp).
|
||||||
|
Post("https://webapi.leigod.com/api/user/info")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("雷神加速器用户信息获取失败: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("雷神加速器用户信息获取结果: %s", unicodeToText(resp.String()))
|
||||||
|
|
||||||
|
// 返回状态码不是0表示有错
|
||||||
|
if userInfoResp.Code != 0 {
|
||||||
|
err = errors.New(userInfoResp.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInfoResp.Data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause
|
||||||
|
// @description: 暂停加速
|
||||||
|
// @receiver l
|
||||||
|
// @return string
|
||||||
|
func (l *leiGod) Pause() (err error) {
|
||||||
|
// 组装参数
|
||||||
|
param := map[string]any{
|
||||||
|
"account_token": l.token,
|
||||||
|
"lang": "zh_CN",
|
||||||
|
"os_type": 4,
|
||||||
|
}
|
||||||
|
pbs, _ := json.Marshal(param)
|
||||||
|
|
||||||
|
var pauseResp model.Response[any]
|
||||||
|
var resp *resty.Response
|
||||||
|
|
||||||
|
res := resty.New()
|
||||||
|
resp, err = res.R().
|
||||||
|
SetHeader("Content-Type", "application/json;chartset=utf-8").
|
||||||
|
SetBody(string(pbs)).
|
||||||
|
SetResult(&pauseResp).
|
||||||
|
Post("https://webapi.leigod.com/api/user/pause")
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("雷神加速器暂停失败: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("雷神加速器暂停结果: %s", unicodeToText(resp.String()))
|
||||||
|
|
||||||
|
// 返回状态码不是0表示有错
|
||||||
|
if pauseResp.Code != 0 {
|
||||||
|
err = errors.New(pauseResp.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
15
utils/string.go
Normal file
15
utils/string.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// unicodeToText
|
||||||
|
// @description: unicode转文本
|
||||||
|
// @param str
|
||||||
|
// @return dst
|
||||||
|
func unicodeToText(str string) (dst string) {
|
||||||
|
dst, _ = strconv.Unquote(strings.Replace(strconv.Quote(str), `\\u`, `\u`, -1))
|
||||||
|
return
|
||||||
|
}
|
||||||
8
vo/leigod.go
Normal file
8
vo/leigod.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package vo
|
||||||
|
|
||||||
|
// LeiGodAccount
|
||||||
|
// @description: 雷神账号
|
||||||
|
type LeiGodAccount struct {
|
||||||
|
Account string `json:"account"` // 账号
|
||||||
|
Password string `json:"password"` // 密码
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user