diff --git a/wclient/README.md b/wclient/README.md index 613c93af..0cd9e989 100644 --- a/wclient/README.md +++ b/wclient/README.md @@ -88,11 +88,11 @@ switch msg.Type { case 0: //朋友圈消息 case 1: //文字 - hook1(msg) + receiver1(msg) case 3: //图片 case 34: //语音 case 37: //好友确认 - hook37(msg) + receiver37(msg) case 40: //POSSIBLEFRIEND_MSG case 42: //名片 case 43: //视频 @@ -107,9 +107,9 @@ switch msg.Type { case 66: //微信红包 case 9999: //SYSNOTICE case 10000: //红包、系统消息 - hook10000(msg) + receiver10000(msg) case 10002: //撤回消息 - hook10002(msg) + receiver10002(msg) case 1048625: //搜狗表情 case 16777265: //链接 case 436207665: //微信红包 diff --git a/wclient/robot/daemon.go b/wclient/robot/daemon.go new file mode 100644 index 00000000..f91191ad --- /dev/null +++ b/wclient/robot/daemon.go @@ -0,0 +1,41 @@ +package robot + +import ( + "github.com/opentdp/go-helper/logman" + "github.com/opentdp/wechat-rest/dbase/setting" + "github.com/opentdp/wechat-rest/wcferry" + "github.com/opentdp/wechat-rest/wclient" +) + +var wc *wcferry.Client + +var clientId string +var selfInfo *wcferry.UserInfo + +func Start() { + + setting.Laod() + + if !setting.BotEnable { + logman.Warn("robot disabled") + return + } + + if clientId != "" { + logman.Warn("robot already started") + return + } + + initHandlers() + + wc = wclient.Register() + clientId, _ = wc.EnrollReceiver(true, receiver) + +} + +func Redo() { + + selfInfo = nil + initHandlers() + +} diff --git a/wclient/robot/handler.go b/wclient/robot/handler.go index 5f406ee6..0d61eeaf 100644 --- a/wclient/robot/handler.go +++ b/wclient/robot/handler.go @@ -4,7 +4,6 @@ import ( "sort" "strings" - "github.com/opentdp/wechat-rest/dbase/chatroom" "github.com/opentdp/wechat-rest/dbase/profile" "github.com/opentdp/wechat-rest/dbase/setting" "github.com/opentdp/wechat-rest/wcferry" @@ -16,8 +15,9 @@ type Handler struct { ChatAble bool // 是否允许在私聊使用 RoomAble bool // 是否允许在群聊使用 Command string // 指令 + Alias string // 指令别名 Describe string // 指令的描述信息 - PreCheck func(*wcferry.WxMsg) string // 前置检查,可拦截所有聊天内容 + PreCheck func(*wcferry.WxMsg) string // 前置检查,可拦截文本聊天内容 Callback func(*wcferry.WxMsg) string // 指令回调,返回回复内容 } @@ -47,18 +47,12 @@ func initHandlers() { }) // 更新列表 - Handlers = list - HandlerMap = lmap + Handlers, HandlerMap = list, lmap } func applyHandlers(msg *wcferry.WxMsg) string { - // 白名单 - if txt := whiteLimit(msg); txt != "" { - return txt - } - // 前置钩子 for _, v := range Handlers { if v.PreCheck != nil { @@ -114,30 +108,3 @@ func applyHandlers(msg *wcferry.WxMsg) string { return handler.Callback(msg) } - -// 白名单模式 -func whiteLimit(msg *wcferry.WxMsg) string { - - if !setting.WhiteLimit { - return "" - } - - // 管理豁免 - up, _ := profile.Fetch(&profile.FetchParam{Wxid: msg.Sender, Roomid: prid(msg)}) - if up.Level >= 7 { - return "" - } - - // 验证权限 - if msg.IsGroup { - room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) - if room.Level < 2 { - return "-" - } - } else if up.Level < 2 { - return "-" - } - - return "" - -} diff --git a/wclient/robot/receiver.go b/wclient/robot/receiver.go index 002f4dda..7d8a278b 100644 --- a/wclient/robot/receiver.go +++ b/wclient/robot/receiver.go @@ -1,276 +1,102 @@ package robot import ( - "encoding/xml" - "regexp" - "strconv" "strings" - "time" - "github.com/opentdp/go-helper/logman" - - "github.com/opentdp/wechat-rest/args" "github.com/opentdp/wechat-rest/dbase/chatroom" - "github.com/opentdp/wechat-rest/dbase/message" + "github.com/opentdp/wechat-rest/dbase/profile" "github.com/opentdp/wechat-rest/dbase/setting" "github.com/opentdp/wechat-rest/wcferry" - "github.com/opentdp/wechat-rest/wcferry/types" ) func receiver(msg *wcferry.WxMsg) { + if whiteLimit(msg) { + return // 白名单限制 + } + switch msg.Type { case 1: //文字 - hook1(msg) + receiver1(msg) case 3: //图片 hook3(msg) case 37: //好友确认 - hook37(msg) + receiver37(msg) case 49: //混合消息 hook49(msg) case 10000: //红包、系统消息 - hook10000(msg) + receiver10000(msg) case 10002: //撤回消息 - hook10002(msg) + receiver10002(msg) } } -// 处理新消息 -func hook1(msg *wcferry.WxMsg) { +// 个人信息 +func self() *wcferry.UserInfo { - // 处理聊天指令 - if msg.IsGroup || wcferry.ContactType(msg.Sender) == "好友" { - output := applyHandlers(msg) - if strings.Trim(output, "-") != "" { - reply(msg, output) - } - return + if selfInfo == nil { + selfInfo = wc.CmdClient.GetSelfInfo() } + return selfInfo } -// 自动保存图片 -func hook3(msg *wcferry.WxMsg) { - - if !setting.AutoSaveImage || msg.Extra == "" { - return - } - - time.Sleep(2 * time.Second) // 等待数据落库 +// 会话场景 +func prid(msg *wcferry.WxMsg) string { - p, err := wc.CmdClient.DownloadImage(msg.Id, msg.Extra, "", 5) - if err != nil || p == "" { - logman.Error("image save failed", "err", err) - return - } - - logman.Info("image saved", "path", p) - if args.Wcf.MsgStore { - message.Update(&message.UpdateParam{Id: msg.Id, Remark: p}) + if msg.IsGroup { + return msg.Roomid } + return "-" } -// 处理新朋友通知 -func hook37(msg *wcferry.WxMsg) { +// 回复消息 +func reply(msg *wcferry.WxMsg, text string) int32 { - // 自动接受新朋友 - if setting.FriendAccept { - ret := types.MsgContent37{} - err := xml.Unmarshal([]byte(msg.Content), &ret) - if err == nil && ret.FromUserName != "" { - wc.CmdClient.AcceptNewFriend(ret.EncryptUserName, ret.Ticket, ret.Scene) - } + if msg.IsSelf { + return -2 } -} - -// 处理混合类消息 -func hook49(msg *wcferry.WxMsg) { - - ret := types.MsgContent49{} - err := xml.Unmarshal([]byte(msg.Content), &ret) - if err != nil { - return + if text = strings.TrimSpace(text); text == "" { + return -1 } - title := ret.AppMsg.Title - refId := ret.AppMsg.ReferMsg.Svrid - - if ret.AppMsg.Type == 57 { - // 撤回引用的消息 - // TDOO: 未实现鉴权 - if strings.HasPrefix(title, "撤回") { - wc.CmdClient.RevokeMsg(refId) - return - } - // 引用图片 - if ret.AppMsg.ReferMsg.Type == 3 { - origin, err := message.Fetch(&message.FetchParam{Id: refId}) - if err == nil && origin.Remark != "" { - msg.Thumb = origin.Remark - } - msg.Content = title - msg.Extra = "image-txt" - hook1(msg) - return - } - // 引用混合类消息 - if ret.AppMsg.ReferMsg.Type == 49 { - origin, err := message.Fetch(&message.FetchParam{Id: refId}) - if err == nil && origin.Content != "" { - msg.Content = title + "\nXML数据如下:\n" + origin.Content - msg.Extra = "record-txt" - hook1(msg) - return - } - return - } + if msg.IsGroup { + user := wc.CmdClient.GetInfoByWxid(msg.Sender) + return wc.CmdClient.SendTxt("@"+user.Name+"\n"+text, msg.Roomid, msg.Sender) + } else { + return wc.CmdClient.SendTxt(text, msg.Sender, "") } } -// 处理系统消息 -func hook10000(msg *wcferry.WxMsg) { - - // 接受好友后响应 - if strings.Contains(msg.Content, "现在可以开始聊天了") { - if len(setting.FriendHello) > 1 { - time.Sleep(2 * time.Second) // 延迟 2 秒 - wc.CmdClient.SendTxt(setting.FriendHello, msg.Sender, "") - } - return - } +// 白名单限制 +// return 验证结果 [true 受限, false 忽略] +func whiteLimit(msg *wcferry.WxMsg) bool { - // 邀请"xxx"加入了群聊 - r1 := regexp.MustCompile(`邀请"(.+)"加入了群聊`) - if matches := r1.FindStringSubmatch(msg.Content); len(matches) > 1 { - room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) - if strings.Trim(room.WelcomeMsg, "-") != "" { - time.Sleep(3 * time.Second) // 延迟 3 秒 - wc.CmdClient.SendTxt("@"+matches[1]+"\n"+room.WelcomeMsg, msg.Roomid, "") - } - return + // 无需验证 + if !setting.WhiteLimit { + return false } - // "xxx"通过扫描"xxx"分享的二维码加入群聊 - r2 := regexp.MustCompile(`"(.+)"通过扫描"(.+)"分享的二维码加入群聊`) - if matches := r2.FindStringSubmatch(msg.Content); len(matches) > 1 { - room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) - if strings.Trim(room.WelcomeMsg, "-") != "" { - time.Sleep(3 * time.Second) // 延迟 3 秒 - wc.CmdClient.SendTxt("@"+matches[1]+"\n"+room.WelcomeMsg, msg.Roomid, "") - } - return + // 管理豁免 + up, _ := profile.Fetch(&profile.FetchParam{Wxid: msg.Sender, Roomid: prid(msg)}) + if up.Level >= 7 { + return false } - // 自动回应拍一拍 - if strings.Contains(msg.Content, "拍了拍我") { - if msg.IsGroup { - room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) - if strings.Trim(room.PatReturn, "-") != "" { - wc.CmdClient.SendPatMsg(msg.Roomid, msg.Sender) - } - } else if setting.PatReturn { - wc.CmdClient.SendPatMsg(msg.Roomid, msg.Sender) - } - return - } - -} - -// 处理撤回消息 -func hook10002(msg *wcferry.WxMsg) { - - var output string - - // 获取撤回提示 + // 验证名单 if msg.IsGroup { room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) - output = room.RevokeMsg - } else { - output = setting.RevokeMsg - } - - // 防撤回提示已关闭 - if len(output) < 2 { - return - } - - // 解析已撤回的消息 - revoke := types.MsgContent10002{} - err := xml.Unmarshal([]byte(msg.Content), &revoke) - if err != nil || revoke.RevokeMsg.NewMsgID == "" { - return - } - - // 获取已撤回消息的 Id - id, err := strconv.Atoi(revoke.RevokeMsg.NewMsgID) - if err != nil || id == 0 { - return - } - - // 取回已撤回的消息内容 - origin, err := message.Fetch(&message.FetchParam{Id: uint64(id)}) - if err != nil || origin.Content == "" { - return - } - - // 提示已撤回的消息内容 - str := strings.TrimSpace(origin.Content) - xmlPrefixes := []string{" 1 { + time.Sleep(2 * time.Second) // 延迟 2 秒 + wc.CmdClient.SendTxt(setting.FriendHello, msg.Sender, "") + } + return + } + + // 邀请"xxx"加入了群聊 + r1 := regexp.MustCompile(`邀请"(.+)"加入了群聊`) + if matches := r1.FindStringSubmatch(msg.Content); len(matches) > 1 { + room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) + if strings.Trim(room.WelcomeMsg, "-") != "" { + time.Sleep(3 * time.Second) // 延迟 3 秒 + wc.CmdClient.SendTxt("@"+matches[1]+"\n"+room.WelcomeMsg, msg.Roomid, "") + } + return + } + + // "xxx"通过扫描"xxx"分享的二维码加入群聊 + r2 := regexp.MustCompile(`"(.+)"通过扫描"(.+)"分享的二维码加入群聊`) + if matches := r2.FindStringSubmatch(msg.Content); len(matches) > 1 { + room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) + if strings.Trim(room.WelcomeMsg, "-") != "" { + time.Sleep(3 * time.Second) // 延迟 3 秒 + wc.CmdClient.SendTxt("@"+matches[1]+"\n"+room.WelcomeMsg, msg.Roomid, "") + } + return + } + + // 自动回应拍一拍 + if strings.Contains(msg.Content, "拍了拍我") { + if msg.IsGroup { + room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) + if strings.Trim(room.PatReturn, "-") != "" { + wc.CmdClient.SendPatMsg(msg.Roomid, msg.Sender) + } + } else if setting.PatReturn { + wc.CmdClient.SendPatMsg(msg.Roomid, msg.Sender) + } + return + } + +} diff --git a/wclient/robot/receiver_10002.go b/wclient/robot/receiver_10002.go new file mode 100644 index 00000000..b4c29981 --- /dev/null +++ b/wclient/robot/receiver_10002.go @@ -0,0 +1,108 @@ +package robot + +import ( + "encoding/xml" + "strconv" + "strings" + + "github.com/opentdp/wechat-rest/dbase/chatroom" + "github.com/opentdp/wechat-rest/dbase/message" + "github.com/opentdp/wechat-rest/dbase/setting" + "github.com/opentdp/wechat-rest/wcferry" + "github.com/opentdp/wechat-rest/wcferry/types" +) + +// 处理撤回消息 +func receiver10002(msg *wcferry.WxMsg) { + + var output string + + // 获取撤回提示 + if msg.IsGroup { + room, _ := chatroom.Fetch(&chatroom.FetchParam{Roomid: msg.Roomid}) + output = room.RevokeMsg + } else { + output = setting.RevokeMsg + } + + // 防撤回提示已关闭 + if len(output) < 2 { + return + } + + // 解析已撤回的消息 + revoke := types.MsgContent10002{} + err := xml.Unmarshal([]byte(msg.Content), &revoke) + if err != nil || revoke.RevokeMsg.NewMsgID == "" { + return + } + + // 获取已撤回消息的 Id + id, err := strconv.Atoi(revoke.RevokeMsg.NewMsgID) + if err != nil || id == 0 { + return + } + + // 取回已撤回的消息内容 + origin, err := message.Fetch(&message.FetchParam{Id: uint64(id)}) + if err != nil || origin.Content == "" { + return + } + + // 提示已撤回的消息内容 + str := strings.TrimSpace(origin.Content) + xmlPrefixes := []string{"