完善微信机器人相关接口(wxhelper接口还未全部实现)

This commit is contained in:
李寻欢 2024-02-05 15:21:36 +08:00
parent 08821977b4
commit 3fbc6780a0
26 changed files with 715 additions and 0 deletions

25
api/admin/robot/delete.go Normal file
View File

@ -0,0 +1,25 @@
package robot
import (
"github.com/gin-gonic/gin"
"unicode/utf8"
"wechat-robot/pkg/response"
robotService "wechat-robot/service/robot"
)
// DeleteById
// @description: 删除机器人
// @param ctx
func DeleteById(ctx *gin.Context) {
var id = ctx.Param("id")
if utf8.RuneCountInString(id) != 32 {
response.New(ctx).SetMsg("参数错误").Fail()
return
}
// 删除数据
if err := robotService.DeleteById(id); err != nil {
response.New(ctx).SetMsg("删除失败").SetError(err).Fail()
} else {
response.New(ctx).SetMsg("删除成功").Success()
}
}

31
api/admin/robot/save.go Normal file
View File

@ -0,0 +1,31 @@
package robot
import (
"github.com/gin-gonic/gin"
robotParam "wechat-robot/model/param/robot"
"wechat-robot/pkg/response"
robotService "wechat-robot/service/robot"
)
// Save
// @description: 保存机器人
// @param ctx
func Save(ctx *gin.Context) {
var p robotParam.Save
if err := ctx.ShouldBind(&p); err != nil {
response.New(ctx).SetMsg("参数错误").SetError(err).Fail()
return
}
// 参数校验
if p.Id == "" && (p.HookApi == "" || p.Version == 0) {
response.New(ctx).SetMsg("参数错误").Fail()
return
}
if err := robotService.Save(p); err != nil {
response.New(ctx).SetMsg("保存失败").SetError(err).Fail()
return
}
response.New(ctx).SetMsg("保存成功").Success()
}

26
api/admin/robot/select.go Normal file
View File

@ -0,0 +1,26 @@
package robot
import (
"github.com/gin-gonic/gin"
robotParam "wechat-robot/model/param/robot"
"wechat-robot/pkg/response"
robotService "wechat-robot/service/robot"
)
// GetAll
// @description: 获取所有机器人
// @param ctx
func GetAll(ctx *gin.Context) {
var p robotParam.GetAll
if err := ctx.ShouldBind(&p); err != nil {
response.New(ctx).SetMsg("参数错误").SetError(err).Fail()
return
}
records, err := robotService.GetAll(p)
if err != nil {
response.New(ctx).SetMsg("数据获取失败").SetError(err).Fail()
return
}
response.New(ctx).SetData(records).Success()
}

1
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.17.0
github.com/go-redis/redis/v8 v8.11.5
github.com/go-resty/resty/v2 v2.11.0
github.com/google/uuid v1.6.0
github.com/mojocn/base64Captcha v1.3.6
github.com/spf13/viper v1.18.2

13
go.sum
View File

@ -315,6 +315,8 @@ github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QX
github.com/go-redis/redis/v8 v8.0.0-beta.5/go.mod h1:Mm9EH/5UMRx680UIryN6rd5XFn/L7zORPqLV+1D5thQ=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -985,6 +987,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1076,6 +1079,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -1176,11 +1181,15 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1190,6 +1199,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
@ -1198,6 +1208,9 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -15,6 +15,10 @@ type Robot struct {
DataSavePath string `json:"dataSavePath" gorm:"type:varchar(255);comment:'微信保存目录'"` // 微信保存目录
DbKey string `json:"dbKey" gorm:"type:varchar(255);comment:'数据库的SQLCipher的加密key'"` // 数据库的SQLCipher的加密key可以使用该key配合decrypt.py解密数据库
HookApi string `json:"hookApi" gorm:"type:varchar(255);comment:'hook接口地址'"` // hook接口地址
Remark string `json:"remark" gorm:"type:varchar(255);comment:'备注'"` // 备注
Version int `json:"version" gorm:"type:int;comment:'版本号'"` // 版本号
VncUrl string `json:"vncUrl" gorm:"type:varchar(255);comment:'vnc地址'"` // vnc地址
Tag string `json:"tag" gorm:"type:varchar(255);comment:'标签'"` // 标签
}
// TableName

View File

@ -0,0 +1,13 @@
package robot
import "wechat-robot/pkg/wxhelper"
// Save
// @description: 保存机器人
type Save struct {
Id string `json:"id" form:"id"` // id
HookApi string `json:"hookApi" form:"hookApi"` // hook接口地址
Version wxhelper.WeChatVersion `json:"version" form:"version"` // 版本
VncUrl string `json:"vncUrl" form:"vncUrl"` // vnc地址
Remark string `json:"remark" form:"remark"` // 备注
}

View File

@ -0,0 +1,8 @@
package robot
// GetAll
// @description: 查询所有机器人入参
type GetAll struct {
Keyword string `json:"keyword" form:"keyword"` // 关键字
Tag string `json:"tag" form:"tag"` // 标签
}

20
model/robot/chatroom.go Normal file
View File

@ -0,0 +1,20 @@
package model
// ChatRoomDetailInfo
// @description: 群聊详情
type ChatRoomDetailInfo struct {
Admin string `json:"admin"` // 群主微信
ChatRoomId string `json:"chatRoomId"` // 群Id
Notice string `json:"notice"` // 群公告
Xml string `json:"xml"` // 不知道是啥玩意儿
}
// GroupUser
// @description: 群成员返回结果
type GroupUser struct {
Admin string `json:"admin"` // 群主微信
AdminNickname string `json:"adminNickname"` // 群主昵称
ChatRoomId string `json:"chatRoomId"` // 群Id
MemberNickname string `json:"memberNickname"` // 成员昵称 `^G`切割
Members string `json:"members"` // 成员Id `^G`切割
}

26
model/robot/friend.go Normal file
View File

@ -0,0 +1,26 @@
package model
// FriendItem
// @description: 好友列表数据
type FriendItem struct {
CustomAccount string `json:"customAccount"` // 微信号
EncryptName string `json:"encryptName"` // 不知道
Nickname string `json:"nickname"` // 昵称
Pinyin string `json:"pinyin"` // 昵称拼音大写首字母
PinyinAll string `json:"pinyinAll"` // 昵称全拼
Reserved1 int `json:"reserved1"` // 未知
Reserved2 int `json:"reserved2"` // 未知
Type int `json:"type"` // 类型
VerifyFlag int `json:"verifyFlag"` // 未知
WxId string `json:"wxid"` // 微信原始Id
}
// ContactProfile
// @description: 好友资料
type ContactProfile struct {
Account string `json:"account"` // 账号
HeadImage string `json:"headImage"` // 头像
Nickname string `json:"nickname"` // 昵称
V3 string `json:"v3"` // v3
WxId string `json:"wxid"` // 微信Id
}

9
model/robot/response.go Normal file
View File

@ -0,0 +1,9 @@
package model
// Response
// @description: 基础返回结构体
type Response[T any] struct {
Code int `json:"code"` // 状态码
Data T `json:"data"` // 数据
Msg string `json:"msg"` // 消息
}

18
model/robot/userinfo.go Normal file
View File

@ -0,0 +1,18 @@
package model
// UserInfo
// @description: 机器人用户信息
type UserInfo struct {
WxId string `json:"wxid"` // 微信Id
Account string `json:"account"` // 微信号
Name string `json:"name"` // 昵称
HeadImage string `json:"headImage"` // 头像
Mobile string `json:"mobile"` // 手机
Signature string `json:"signature"` // 个人签名
Country string `json:"country"` // 国家
Province string `json:"province"` // 省
City string `json:"city"` // 城市
CurrentDataPath string `json:"currentDataPath"` // 当前数据目录,登录的账号目录
DataSavePath string `json:"dataSavePath"` // 微信保存目录
DbKey string `json:"dbKey"` // 数据库的SQLCipher的加密key可以使用该key配合decrypt.py解密数据库
}

44
pkg/wxhelper/base.go Normal file
View File

@ -0,0 +1,44 @@
package wxhelper
import (
"errors"
robotModel "wechat-robot/model/robot"
"wechat-robot/utils"
)
// CheckLogin
// @description: 检查是否登录
// @receiver wx
// @return flag
func (wx wxHelper) CheckLogin() (flag bool) {
var api string
if api, flag = wx.version.GetApi("CheckLogin"); !flag {
return
}
// 调用接口
var resp robotModel.Response[any]
_ = utils.HttpClientUtils().Post(wx.host+api, nil, &resp, 0)
return resp.Code == 1
}
// UserInfo
// @description: 获取机器人信息
// @receiver wx
// @return data
// @return err
func (wx wxHelper) UserInfo() (data robotModel.UserInfo, err error) {
var api string
var flag bool
if api, flag = wx.version.GetApi("UserInfo"); !flag {
err = errors.New("不支持的接口")
return
}
// 调用接口
var resp robotModel.Response[robotModel.UserInfo]
if err = utils.HttpClientUtils().Post(wx.host+api, nil, &resp, 0); err != nil {
return
}
data = resp.Data
return
}

61
pkg/wxhelper/chatroom.go Normal file
View File

@ -0,0 +1,61 @@
package wxhelper
import robotModel "wechat-robot/model/robot"
// GetChatRoomDetailInfo
// @description: 获取群聊详情
// @receiver wx
// @param chatRoomId
// @return data
func (wx wxHelper) GetChatRoomDetailInfo(chatRoomId string) (data robotModel.ChatRoomDetailInfo) {
return
}
// AddMemberToChatRoom
// @description: 添加群成员
// @receiver wx
// @param chatRoomId
// @param memberWxIds
// @return err
func (wx wxHelper) AddMemberToChatRoom(chatRoomId string, memberWxIds []string) (err error) {
return
}
// DelMemberFromChatRoom
// @description: 删除群成员
// @receiver wx
// @param chatRoomId
// @param memberWxIds
// @return err
func (wx wxHelper) DelMemberFromChatRoom(chatRoomId string, memberWxIds []string) (err error) {
return
}
// GetMemberFromChatRoom
// @description: 获取群成员
// @receiver wx
// @param chatRoomId
// @return data
func (wx wxHelper) GetMemberFromChatRoom(chatRoomId string) (data robotModel.GroupUser) {
return
}
// InviteMemberToChatRoom
// @description: 邀请入群
// @receiver wx
// @param chatRoomId
// @param memberWxIds
// @return err
func (wx wxHelper) InviteMemberToChatRoom(chatRoomId string, memberWxIds []string) (err error) {
return
}
// ModifyChatRoomNickname
// @description: 修改群内昵称
// @receiver wx
// @param wxId
// @param nickname
// @return err
func (wx wxHelper) ModifyChatRoomNickname(wxId, nickname string) (err error) {
return
}

79
pkg/wxhelper/constant.go Normal file
View File

@ -0,0 +1,79 @@
package wxhelper
// WeChatVersion 微信版本
type WeChatVersion int
// 微信版本
const (
WechatVersion39223 WeChatVersion = 39223
WechatVersion39581 WeChatVersion = 39581
WechatVersion39825 WeChatVersion = 39825
)
// 接口映射
var apiMap = map[WeChatVersion]map[string]string{
WechatVersion39223: v39223ApiMap,
WechatVersion39581: v39581ApiMap,
WechatVersion39825: v39581ApiMap,
}
var v39223ApiMap = map[string]string{
"CheckLogin": "/api/?type=0",
"UserInfo": "/api/?type=1",
"SendTextMsg": "/api/?type=2",
"SendAtTest": "/api/?type=3",
"SendFileMsg": "/api/?type=6",
"SendImagesMsg": "/api/?type=5",
"SendCustomEmotionMsg": "",
"GetContactList": "/api/?type=46",
"GetChatRoomDetailInfo": "/api/?type=47",
"AddMemberToChatRoom": "/api/?type=28",
"DelMemberFromChatRoom": "/api/?type=27",
"GetMemberFromChatRoom": "/api/?type=25",
"GetContactProfile": "/api/?type=25",
"InviteMemberToChatRoom": "",
"ModifyChatRoomNickname": "/api/?type=31",
"StartHook": "/api/?type=9",
"StopHook": "/api/?type=10",
"DownloadAttach": "/api/?type=56",
"DecodeImage": "/api/?type=48",
}
var v39581ApiMap = map[string]string{
"CheckLogin": "/api/checkLogin",
"UserInfo": "/api/userInfo",
"SendTextMsg": "/api/sendTextMsg",
"SendAtTest": "/api/sendAtText",
"SendFileMsg": "/api/sendFileMsg",
"SendImagesMsg": "/api/sendImagesMsg",
"SendCustomEmotionMsg": "/api/sendCustomEmotion",
"GetContactList": "/api/getContactList",
"GetChatRoomDetailInfo": "/api/getChatRoomDetailInfo",
"AddMemberToChatRoom": "/api/addMemberToChatRoom",
"DelMemberFromChatRoom": "/api/delMemberFromChatRoom",
"GetMemberFromChatRoom": "/api/getMemberFromChatRoom",
"GetContactProfile": "/api/getContactProfile",
"InviteMemberToChatRoom": "/api/InviteMemberToChatRoom",
"ModifyChatRoomNickname": "/api/modifyNickname",
"QuitChatRoom": "/api/quitChatRoom",
"StartHook": "/api/hookSyncMsg",
"StopHook": "/api/unhookSyncMsg",
"DownloadAttach": "/api/downloadAttach",
"DecodeImage": "/api/decodeImage",
}
// GetApi
// @description: 获取接口地址
// @receiver v
// @return string
func (v WeChatVersion) GetApi(code string) (api string, ok bool) {
var am map[string]string
// 判断版本是否支持
if am, ok = apiMap[v]; !ok {
return
}
// 获取接口
api, ok = am[code]
return
}

21
pkg/wxhelper/contact.go Normal file
View File

@ -0,0 +1,21 @@
package wxhelper
import robotModel "wechat-robot/model/robot"
// GetContactList
// @description: 获取联系人列表
// @receiver wx
// @return []robotModel.FriendItem
func (wx wxHelper) GetContactList() (friends []robotModel.FriendItem) {
return
}
// GetContactProfile
// @description: 获取好友资料
// @receiver wx
// @param wxId
// @return robotModel.ContactProfile
func (wx wxHelper) GetContactProfile(wxId string) (profile robotModel.ContactProfile) {
return
}

20
pkg/wxhelper/file.go Normal file
View File

@ -0,0 +1,20 @@
package wxhelper
// DownloadAttach
// @description: 下载附件
// @receiver wx
// @param msgId
// @return err
func (wx wxHelper) DownloadAttach(msgId int) (err error) {
return
}
// DecodeImage
// @description: 解码图片
// @receiver wx
// @param filePath
// @param storeDir
// @return err
func (wx wxHelper) DecodeImage(filePath, storeDir string) (err error) {
return
}

19
pkg/wxhelper/hook.go Normal file
View File

@ -0,0 +1,19 @@
package wxhelper
// StartHook
// @description: 启动hook
// @receiver wx
// @param tcpIp
// @param tcpPort
// @return err
func (wx wxHelper) StartHook(tcpIp string, tcpPort int) (err error) {
return
}
// StopHook
// @description: 停止hook
// @receiver wx
// @return err
func (wx wxHelper) StopHook() (err error) {
return
}

46
pkg/wxhelper/message.go Normal file
View File

@ -0,0 +1,46 @@
package wxhelper
// SendTextMsg
// @description: 发送文字消息
// @receiver wx
// @param toUserId
// @param atUserId
// @param msg
// @return err
func (wx wxHelper) SendTextMsg(toUserId, atUserId, msg string) (err error) {
return
}
// SendFileMsg
// @description: 发送文件消息
// @receiver wx
// @param toUserId
// @param filePath
// @return err
func (wx wxHelper) SendFileMsg(toUserId, filePath string) (err error) {
return
}
// SendImagesMsg
// @description: 发送图片消息
// @receiver wx
// @param toUserId
// @param imagePath
// @return err
func (wx wxHelper) SendImagesMsg(toUserId, imagePath string) (err error) {
return
}
// SendCustomEmotionMsg
// @description: 发送自定义表情消息
// @receiver wx
// @param toUserId
// @param emotionPath
// @return err
func (wx wxHelper) SendCustomEmotionMsg(toUserId string, emotionPath string) (err error) {
return
}

52
pkg/wxhelper/wxhelper.go Normal file
View File

@ -0,0 +1,52 @@
package wxhelper
import (
"errors"
"slices"
robotModel "wechat-robot/model/robot"
)
type wxHelper struct {
host string // hook接口地址
version WeChatVersion // 微信版本
}
// Api
// @description: 微信助手接口
type Api interface {
CheckLogin() bool // 检查是否登录
UserInfo() (robotModel.UserInfo, error) // 获取机器人信息
SendTextMsg(toUserId, atUserId, msg string) error // 发送文字消息
SendFileMsg(toUserId, filePath string) error // 发送文件消息
SendImagesMsg(toUserId, imagePath string) error // 发送图片消息
SendCustomEmotionMsg(toUserId string, emotionPath string) error // 发送自定义表情消息
GetContactList() []robotModel.FriendItem // 获取联系人列表
GetChatRoomDetailInfo(chatRoomId string) robotModel.ChatRoomDetailInfo // 获取群聊详情
AddMemberToChatRoom(chatRoomId string, memberWxIds []string) error // 添加群成员
DelMemberFromChatRoom(chatRoomId string, memberWxIds []string) error // 删除群成员
GetMemberFromChatRoom(chatRoomId string) robotModel.GroupUser // 获取群成员
GetContactProfile(wxId string) robotModel.ContactProfile // 获取好友资料
InviteMemberToChatRoom(chatRoomId string, memberWxIds []string) error // 邀请入群
ModifyChatRoomNickname(wxId, nickname string) error // 修改群内昵称
StartHook(tcpIp string, tcpPort int) error // 开启消息监听 - 暂时只支持tcp因为http有点儿不太好使
StopHook() error // 关闭消息监听
DownloadAttach(msgId int) error // 下载附件
DecodeImage(filePath, storeDir string) error // 解码图片
}
// New
// @description: 创建微信助手
// @param host
// @return Api
func New(host string, version WeChatVersion) (Api, error) {
// 判断微信版本是否支持
keys := make([]WeChatVersion, 0, len(apiMap))
for k := range apiMap {
keys = append(keys, k)
}
if !slices.Contains(keys, version) {
return nil, errors.New("不支持的微信版本")
}
return &wxHelper{host: host, version: version}, nil
}

15
router/admin/robot.go Normal file
View File

@ -0,0 +1,15 @@
package admin
import (
"github.com/gin-gonic/gin"
robotApi "wechat-robot/api/admin/robot"
)
// robot
// @description: 机器人相关接口
// @param g
func robot(g *gin.RouterGroup) {
g.GET("", robotApi.GetAll) // 获取所有机器人
g.POST("", robotApi.Save) // 保存机器人
g.DELETE("/:id", robotApi.DeleteById) // 删除机器人
}

View File

@ -13,5 +13,6 @@ func InitRoute(g *gin.RouterGroup) {
g.Use(middleware.AuthorizeToken())
menu(g.Group("/menu")) // 菜单相关
robot(g.Group("/robot")) // 机器人相关
aiAssistant(g.Group("/ai-assistant")) // AI助手相关
}

16
service/robot/delete.go Normal file
View File

@ -0,0 +1,16 @@
package robot
import (
"wechat-robot/internal/database"
"wechat-robot/model/entity"
)
// DeleteById
// @description: 删除机器人
// @param id
// @return err
func DeleteById(id string) (err error) {
// 删除数据
err = database.Client.Where("id = ?", id).Delete(&entity.Robot{}).Error
return
}

70
service/robot/save.go Normal file
View File

@ -0,0 +1,70 @@
package robot
import (
"errors"
"log"
"wechat-robot/internal/database"
"wechat-robot/model/entity"
robotParam "wechat-robot/model/param/robot"
"wechat-robot/pkg/wxhelper"
)
// Save
// @description: 保存机器人
// @param p
// @return err
func Save(p robotParam.Save) (err error) {
if p.Id != "" {
// 是修改,暂时只支持修改备注
err = database.Client.Model(&entity.Robot{}).Where("id = ?", p.Id).Update("remark", p.Remark).Error
return
}
// 新增
// 获取机器人信息,如果获取失败,就返回错误信息
cli, err := wxhelper.New(p.HookApi, p.Version)
if err != nil {
return
}
isLogin := cli.CheckLogin()
log.Printf("机器人是否登录: %v", isLogin)
if !isLogin {
err = errors.New("机器人未登录")
return
}
// 取出登录账号信息
info, err := cli.UserInfo()
if err != nil {
return
}
log.Printf("机器人Id: %s", info.WxId)
log.Printf("机器人微信号: %s", info.Account)
log.Printf("机器人名称: %s", info.Name)
// 先查询是否存在
var ent entity.Robot
var count int64
if err = database.Client.Model(&ent).Where("wxid = ?", info.WxId).Count(&count).Error; err != nil {
return
}
if count > 0 {
err = errors.New("机器人已存在")
return
}
ent.WxId = info.WxId
ent.Account = info.Account
ent.Nickname = info.Name
ent.Avatar = info.HeadImage
ent.Mobile = info.Mobile
ent.CurrentDataPath = info.CurrentDataPath
ent.DataSavePath = info.DataSavePath
ent.DbKey = info.DbKey
ent.HookApi = p.HookApi
ent.Remark = p.Remark
ent.Version = int(p.Version)
ent.VncUrl = p.VncUrl
err = database.Client.Create(&ent).Error
return
}

View File

@ -1 +1,25 @@
package robot
import (
"database/sql"
"wechat-robot/internal/database"
"wechat-robot/model/entity"
robotParam "wechat-robot/model/param/robot"
)
// GetAll
// @description: 查询所有机器人
// @param p robotParam.GetAll 查询所有机器人入参
// @return records []entity.Robot 查询所有机器人返回值
// @return err error 查询所有机器人返回值
func GetAll(p robotParam.GetAll) (records []entity.Robot, err error) {
tx := database.Client.Order("created_at desc")
if p.Keyword != "" {
tx.Where("nickname LIKE @keyword OR remark LIKE @keyword", sql.Named("keyword", "%"+p.Keyword+"%"))
}
if p.Tag != "" {
tx.Where("tag LIKE ?", "%"+p.Tag+"%")
}
err = tx.Find(&records).Error
return
}

53
utils/httpclient.go Normal file
View File

@ -0,0 +1,53 @@
package utils
import (
"encoding/json"
"github.com/go-resty/resty/v2"
"log"
"time"
)
type HttpClient interface {
Post(api string, param map[string]any, response any, retryCount int) (err error)
}
type httpClient struct{}
// HttpClientUtils
// @description: Http客户端工具
// @return HttpClient
func HttpClientUtils() HttpClient {
return &httpClient{}
}
// Post
// @description: 发送Post请求
// @receiver c
// @param api
// @param param
// @param response
// @param retryCount
// @return err
func (c httpClient) Post(api string, param map[string]any, response any, retryCount int) (err error) {
if retryCount > 5 {
log.Printf("重试五次失败,停止发送")
return
}
pbs, _ := json.Marshal(param)
var r *resty.Response
r, err = resty.New().R().
SetHeader("Content-Type", "application/json;chartset=utf-8").
SetBody(string(pbs)).
SetResult(response).
Post(api)
if err != nil {
log.Printf("请求失败: %s", err.Error())
// 休眠五秒后重新发送
time.Sleep(5 * time.Second)
return c.Post(api, param, response, retryCount+1)
}
log.Printf("请求结果: %s", r.String())
return
}