增加vendor

This commit is contained in:
hoteas
2022-05-24 13:49:25 +08:00
parent 77753a123f
commit b638d8bd65
742 changed files with 509206 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
企业微信会话存档SDK(基于企业微信C版官方SDK封装),暂时只支持在`linux`环境下使用当前SDK。
### 官方文档地址
https://open.work.weixin.qq.com/api/doc/90000/90135/91774
### 使用方式
1、安装 go module
> go get -u github.com/silenceper/wechat/v2
2、从 `github.com/silenceper/wechat/v2/work/msgaudit/lib` 文件夹下复制 `libWeWorkFinanceSdk_C.so` 动态库文件到系统动态链接库默认文件夹下,或者复制到任意文件夹并在当前文件夹下执行 `export LD_LIBRARY_PATH=$(pwd)`命令设置动态链接库检索地址后即可正常使用
3、编译要求
- 开启CGO: `CGO_ENABLED=1`
- 增加tags参数`msgaudit` `go build -tags msgaudit`或者`go run -tags msgaudit main.go`
### Example
```go
package main
import (
"bytes"
"fmt"
"github.com/silenceper/wechat/v2"
"github.com/silenceper/wechat/v2/work/msgaudit"
"github.com/silenceper/wechat/v2/work/config"
"io/ioutil"
"os"
"path"
)
func main() {
//初始化客户端
wechatClient := wechat.NewWechat()
workClient := wechatClient.GetWork(&config.Config{
CorpID: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
CorpSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
RasPrivateKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
})
client, err := workClient.GetMsgAudit()
if err != nil {
fmt.Printf("SDK 初始化失败:%v \n", err)
return
}
//同步消息
chatDataList, err := client.GetChatData(0, 100, "", "", 3)
if err != nil {
fmt.Printf("消息同步失败:%v \n", err)
return
}
for _, chatData := range chatDataList {
//消息解密
chatInfo, err := client.DecryptData(chatData.EncryptRandomKey, chatData.EncryptChatMsg)
if err != nil {
fmt.Printf("消息解密失败:%v \n", err)
return
}
if chatInfo.Type == "image" {
image, _ := chatInfo.GetImageMessage()
sdkFileID := image.Image.SdkFileID
isFinish := false
buffer := bytes.Buffer{}
indexBuf := ""
for !isFinish {
//获取媒体数据
mediaData, err := client.GetMediaData(indexBuf, sdkFileID, "", "", 5)
if err != nil {
fmt.Printf("媒体数据拉取失败:%v \n", err)
return
}
buffer.Write(mediaData.Data)
if mediaData.IsFinish {
isFinish = mediaData.IsFinish
}
indexBuf = mediaData.OutIndexBuf
}
filePath, _ := os.Getwd()
filePath = path.Join(filePath, "test.png")
err := ioutil.WriteFile(filePath, buffer.Bytes(), 0666)
if err != nil {
fmt.Printf("文件存储失败:%v \n", err)
return
}
break
}
}
//释放SDK实例
client.Free()
}
```
+207
View File
@@ -0,0 +1,207 @@
package msgaudit
import "encoding/json"
// ChatDataResponse 会话存档消息响应数据
type ChatDataResponse struct {
Error
ChatDataList []ChatData `json:"chatdata,omitempty"`
}
// IsError 判断是否正确响应
func (c ChatDataResponse) IsError() bool {
return c.ErrCode != 0
}
// ChatData 会话存档原始数据
type ChatData struct {
Seq uint64 `json:"seq,omitempty"` // 消息的seq值,标识消息的序号。再次拉取需要带上上次回包中最大的seq。Uint64类型,范围0-pow(2,64)-1
MsgID string `json:"msgid,omitempty"` // 消息id,消息的唯一标识,企业可以使用此字段进行消息去重。
PublickeyVer uint32 `json:"publickey_ver,omitempty"` // 加密此条消息使用的公钥版本号。
EncryptRandomKey string `json:"encrypt_random_key,omitempty"` // 使用publickey_ver指定版本的公钥进行非对称加密后base64加密的内容,需要业务方先base64 decode处理后,再使用指定版本的私钥进行解密,得出内容。
EncryptChatMsg string `json:"encrypt_chat_msg,omitempty"` // 消息密文。需要业务方使用将encrypt_random_key解密得到的内容,与encrypt_chat_msg,传入sdk接口DecryptData,得到消息明文。
}
// ChatMessage 会话存档消息
type ChatMessage struct {
ID string // 消息id,消息的唯一标识,企业可以使用此字段进行消息去重。
From string // 消息发送方id。同一企业内容为userid,非相同企业为external_userid。消息如果是机器人发出,也为external_userid。
ToList []string // 消息接收方列表,可能是多个,同一个企业内容为userid,非相同企业为external_userid。
Action string // 消息动作,目前有send(发送消息)/recall(撤回消息)/switch(切换企业日志)三种类型。
Type string // 消息类型
originData []byte // 原始消息对象
}
// GetOriginMessage 获取消息原始数据
func (c ChatMessage) GetOriginMessage() (msg map[string]interface{}, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetTextMessage 获取文本消息
func (c ChatMessage) GetTextMessage() (msg TextMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetImageMessage 获取图片消息
func (c ChatMessage) GetImageMessage() (msg ImageMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetRevokeMessage 获取撤回消息
func (c ChatMessage) GetRevokeMessage() (msg RevokeMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetAgreeMessage 获取同意会话聊天内容
func (c ChatMessage) GetAgreeMessage() (msg AgreeMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetVoiceMessage 获取语音消息
func (c ChatMessage) GetVoiceMessage() (msg VoiceMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetVideoMessage 获取视频消息
func (c ChatMessage) GetVideoMessage() (msg VideoMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetCardMessage 获取名片消息
func (c ChatMessage) GetCardMessage() (msg CardMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetLocationMessage 获取位置消息
func (c ChatMessage) GetLocationMessage() (msg LocationMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetEmotionMessage 获取表情消息
func (c ChatMessage) GetEmotionMessage() (msg EmotionMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetFileMessage 获取文件消息
func (c ChatMessage) GetFileMessage() (msg FileMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetLinkMessage 获取链接消息
func (c ChatMessage) GetLinkMessage() (msg LinkMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetWeappMessage 获取小程序消息
func (c ChatMessage) GetWeappMessage() (msg WeappMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetChatRecordMessage 获取会话记录消息
func (c ChatMessage) GetChatRecordMessage() (msg ChatRecordMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetTodoMessage 获取待办消息
func (c ChatMessage) GetTodoMessage() (msg TodoMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetVoteMessage 获取投票消息
func (c ChatMessage) GetVoteMessage() (msg VoteMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetCollectMessage 获取填表消息
func (c ChatMessage) GetCollectMessage() (msg CollectMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetRedpacketMessage 获取红包消息
func (c ChatMessage) GetRedpacketMessage() (msg RedpacketMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetMeetingMessage 获取会议邀请消息
func (c ChatMessage) GetMeetingMessage() (msg MeetingMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetDocMessage 获取在线文档消息
func (c ChatMessage) GetDocMessage() (msg DocMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetMarkdownMessage 获取MarkDown格式消息
func (c ChatMessage) GetMarkdownMessage() (msg MarkdownMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetNewsMessage 获取图文消息
func (c ChatMessage) GetNewsMessage() (msg NewsMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetCalendarMessage 获取日程消息
func (c ChatMessage) GetCalendarMessage() (msg CalendarMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetMixedMessage 获取混合消息
func (c ChatMessage) GetMixedMessage() (msg MixedMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetMeetingVoiceCallMessage 获取音频存档消息
func (c ChatMessage) GetMeetingVoiceCallMessage() (msg MeetingVoiceCallMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetVoipDocShareMessage 获取音频共享消息
func (c ChatMessage) GetVoipDocShareMessage() (msg VoipDocShareMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetExternalRedPacketMessage 获取互通红包消息
func (c ChatMessage) GetExternalRedPacketMessage() (msg ExternalRedPacketMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetSphFeedMessage 获取视频号消息
func (c ChatMessage) GetSphFeedMessage() (msg SphFeedMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
// GetSwitchMessage 获取切换企业日志
func (c ChatMessage) GetSwitchMessage() (msg SwitchMessage, err error) {
err = json.Unmarshal(c.originData, &msg)
return msg, err
}
+265
View File
@@ -0,0 +1,265 @@
//go:build linux && cgo && msgaudit
// +build linux,cgo,msgaudit
// Package msgaudit only for linux
package msgaudit
// #cgo LDFLAGS: -L${SRCDIR}/lib -lWeWorkFinanceSdk_C
// #cgo CFLAGS: -I ./lib/
// #include <stdlib.h>
// #include "WeWorkFinanceSdk_C.h"
import "C"
import (
"encoding/json"
"unsafe"
"github.com/silenceper/wechat/v2/util"
"github.com/silenceper/wechat/v2/work/config"
)
// Client 会话存档
type Client struct {
ptr *C.WeWorkFinanceSdk_t
privateKey string
}
// NewClient 初始会话会话存档实例
/**
* 初始化函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] corpid 调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
* @param [in] secret 聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
* @param [in] privateKey 消息加密私钥,可以在企业微信管理端--管理工具--消息加密公钥查看对用公钥,私钥一般由自己保存
*
*
* @return 返回是否初始化成功
* 0 - 成功
* !=0 - 失败
*/
func NewClient(cfg *config.Config) (*Client, error) {
ptr := C.NewSdk()
corpIDC := C.CString(cfg.CorpID)
corpSecretC := C.CString(cfg.CorpSecret)
defer func() {
C.free(unsafe.Pointer(corpIDC))
C.free(unsafe.Pointer(corpSecretC))
}()
retC := C.Init(ptr, corpIDC, corpSecretC)
ret := int(retC)
if ret != 0 {
return nil, NewSDKErr(ret)
}
return &Client{
ptr: ptr,
privateKey: cfg.RasPrivateKey,
}, nil
}
// Free 释放SDK实例是可调用该方法释放内存
func (s *Client) Free() {
if s.ptr == nil {
return
}
C.DestroySdk(s.ptr)
s.ptr = nil
}
// GetChatData 拉取聊天记录函数
/**
* 拉取聊天记录函数
*
*
* @param [in] seq 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0
* @param [in] limit 一次拉取的消息条数,最大值1000条,超过1000条会返回错误
* @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
* @param [in] timeout 超时时间,单位秒
* @return chatDatas 返回本次拉取消息的数据,slice结构体.内容包括errcode/errmsg,以及每条消息内容。示例如下:
{"errcode":0,"errmsg":"ok","chatdata":[{"seq":196,"msgid":"CAQQ2fbb4QUY0On2rYSAgAMgip/yzgs=","publickey_ver":3,"encrypt_random_key":"ftJ+uz3n/z1DsxlkwxNgE+mL38H42/KCvN8T60gbbtPD+Rta1hKTuQPzUzO6Hzne97MgKs7FfdDxDck/v8cDT6gUVjA2tZ/M7euSD0L66opJ/IUeBtpAtvgVSD5qhlaQjvfKJc/zPMGNK2xCLFYqwmQBZXbNT7uA69Fflm512nZKW/piK2RKdYJhRyvQnA1ISxK097sp9WlEgDg250fM5tgwMjujdzr7ehK6gtVBUFldNSJS7ndtIf6aSBfaLktZgwHZ57ONewWq8GJe7WwQf1hwcDbCh7YMG8nsweEwhDfUz+u8rz9an+0lgrYMZFRHnmzjgmLwrR7B/32Qxqd79A==","encrypt_chat_msg":"898WSfGMnIeytTsea7Rc0WsOocs0bIAerF6de0v2cFwqo9uOxrW9wYe5rCjCHHH5bDrNvLxBE/xOoFfcwOTYX0HQxTJaH0ES9OHDZ61p8gcbfGdJKnq2UU4tAEgGb8H+Q9n8syRXIjaI3KuVCqGIi4QGHFmxWenPFfjF/vRuPd0EpzUNwmqfUxLBWLpGhv+dLnqiEOBW41Zdc0OO0St6E+JeIeHlRZAR+E13Isv9eS09xNbF0qQXWIyNUi+ucLr5VuZnPGXBrSfvwX8f0QebTwpy1tT2zvQiMM2MBugKH6NuMzzuvEsXeD+6+3VRqL"}]}
*/
func (s *Client) GetChatData(seq uint64, limit uint64, proxy string, passwd string, timeout int) ([]ChatData, error) {
proxyC := C.CString(proxy)
passwdC := C.CString(passwd)
chatSlice := C.NewSlice()
defer func() {
C.free(unsafe.Pointer(proxyC))
C.free(unsafe.Pointer(passwdC))
C.FreeSlice(chatSlice)
}()
if s.ptr == nil {
return nil, NewSDKErr(10002)
}
retC := C.GetChatData(s.ptr, C.ulonglong(seq), C.uint(limit), proxyC, passwdC, C.int(timeout), chatSlice)
ret := int(retC)
if ret != 0 {
return nil, NewSDKErr(ret)
}
buf := s.GetContentFromSlice(chatSlice)
var data ChatDataResponse
err := json.Unmarshal(buf, &data)
if err != nil {
return nil, err
}
if data.ErrCode != 0 {
return nil, data.Error
}
return data.ChatDataList, nil
}
// GetRawChatData 拉取聊天记录函数
/**
* 拉取聊天记录函数
*
*
* @param [in] seq 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0
* @param [in] limit 一次拉取的消息条数,最大值1000条,超过1000条会返回错误
* @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
* @param [in] timeout 超时时间,单位秒
* @return chatDatas 返回本次拉取消息的数据,slice结构体.内容包括errcode/errmsg,以及每条消息内容。示例如下:
{"errcode":0,"errmsg":"ok","chatdata":[{"seq":196,"msgid":"CAQQ2fbb4QUY0On2rYSAgAMgip/yzgs=","publickey_ver":3,"encrypt_random_key":"ftJ+uz3n/z1DsxlkwxNgE+mL38H42/KCvN8T60gbbtPD+Rta1hKTuQPzUzO6Hzne97MgKs7FfdDxDck/v8cDT6gUVjA2tZ/M7euSD0L66opJ/IUeBtpAtvgVSD5qhlaQjvfKJc/zPMGNK2xCLFYqwmQBZXbNT7uA69Fflm512nZKW/piK2RKdYJhRyvQnA1ISxK097sp9WlEgDg250fM5tgwMjujdzr7ehK6gtVBUFldNSJS7ndtIf6aSBfaLktZgwHZ57ONewWq8GJe7WwQf1hwcDbCh7YMG8nsweEwhDfUz+u8rz9an+0lgrYMZFRHnmzjgmLwrR7B/32Qxqd79A==","encrypt_chat_msg":"898WSfGMnIeytTsea7Rc0WsOocs0bIAerF6de0v2cFwqo9uOxrW9wYe5rCjCHHH5bDrNvLxBE/xOoFfcwOTYX0HQxTJaH0ES9OHDZ61p8gcbfGdJKnq2UU4tAEgGb8H+Q9n8syRXIjaI3KuVCqGIi4QGHFmxWenPFfjF/vRuPd0EpzUNwmqfUxLBWLpGhv+dLnqiEOBW41Zdc0OO0St6E+JeIeHlRZAR+E13Isv9eS09xNbF0qQXWIyNUi+ucLr5VuZnPGXBrSfvwX8f0QebTwpy1tT2zvQiMM2MBugKH6NuMzzuvEsXeD+6+3VRqL"}]}
*/
func (s *Client) GetRawChatData(seq uint64, limit uint64, proxy string, passwd string, timeout int) (ChatDataResponse, error) {
proxyC := C.CString(proxy)
passwdC := C.CString(passwd)
chatSlice := C.NewSlice()
defer func() {
C.free(unsafe.Pointer(proxyC))
C.free(unsafe.Pointer(passwdC))
C.FreeSlice(chatSlice)
}()
if s.ptr == nil {
return ChatDataResponse{}, NewSDKErr(10002)
}
retC := C.GetChatData(s.ptr, C.ulonglong(seq), C.uint(limit), proxyC, passwdC, C.int(timeout), chatSlice)
ret := int(retC)
if ret != 0 {
return ChatDataResponse{}, NewSDKErr(ret)
}
buf := s.GetContentFromSlice(chatSlice)
var data ChatDataResponse
err := json.Unmarshal(buf, &data)
if err != nil {
return ChatDataResponse{}, err
}
return data, nil
}
// DecryptData 解析密文.企业微信自有解密内容
/**
* @brief 解析密文.企业微信自有解密内容
* @param [in] encrypt_key, getchatdata返回的encrypt_random_key,使用企业自持对应版本秘钥RSA解密后的内容
* @param [in] encrypt_msg, getchatdata返回的encrypt_chat_msg
* @param [out] msg, 解密的消息明文
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
*/
func (s *Client) DecryptData(encryptRandomKey string, encryptMsg string) (msg ChatMessage, err error) {
encryptKey, err := util.RSADecryptBase64(s.privateKey, encryptRandomKey)
if err != nil {
return msg, err
}
encryptKeyC := C.CString(string(encryptKey))
encryptMsgC := C.CString(encryptMsg)
msgSlice := C.NewSlice()
defer func() {
C.free(unsafe.Pointer(encryptKeyC))
C.free(unsafe.Pointer(encryptMsgC))
C.FreeSlice(msgSlice)
}()
retC := C.DecryptData(encryptKeyC, encryptMsgC, msgSlice)
ret := int(retC)
if ret != 0 {
return msg, NewSDKErr(ret)
}
buf := s.GetContentFromSlice(msgSlice)
// handle illegal escape character in text
for i := 0; i < len(buf); {
if buf[i] < 0x20 {
buf = append(buf[:i], buf[i+1:]...)
continue
}
i++
}
var baseMessage BaseMessage
err = json.Unmarshal(buf, &baseMessage)
if err != nil {
return msg, err
}
msg.ID = baseMessage.MsgID
msg.From = baseMessage.From
msg.ToList = baseMessage.ToList
msg.Action = baseMessage.Action
msg.Type = baseMessage.MsgType
msg.originData = buf
return msg, err
}
// GetMediaData 拉取媒体消息函数
/**
* 拉取媒体消息函数
* Return值=0表示该API调用成功
*
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] sdkFileid 从GetChatData返回的聊天消息中,媒体消息包括的sdkfileid
* @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
* @param [in] indexbuf 媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,默认拉取512k,后续每次调用只需要将上次调用返回的outindexbuf填入即可。
* @param [in] timeout 超时时间,单位秒
* @param [out] media_data 返回本次拉取的媒体数据.MediaData结构体.内容包括data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记)
*
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
*/
func (s *Client) GetMediaData(indexBuf string, sdkFileID string, proxy string, passwd string, timeout int) (*MediaData, error) {
indexBufC := C.CString(indexBuf)
sdkFileIDC := C.CString(sdkFileID)
proxyC := C.CString(proxy)
passwdC := C.CString(passwd)
mediaDataC := C.NewMediaData()
defer func() {
C.free(unsafe.Pointer(indexBufC))
C.free(unsafe.Pointer(sdkFileIDC))
C.free(unsafe.Pointer(proxyC))
C.free(unsafe.Pointer(passwdC))
C.FreeMediaData(mediaDataC)
}()
if s.ptr == nil {
return nil, NewSDKErr(10002)
}
retC := C.GetMediaData(s.ptr, indexBufC, sdkFileIDC, proxyC, passwdC, C.int(timeout), mediaDataC)
ret := int(retC)
if ret != 0 {
return nil, NewSDKErr(ret)
}
return &MediaData{
OutIndexBuf: C.GoString(C.GetOutIndexBuf(mediaDataC)),
Data: C.GoBytes(unsafe.Pointer(C.GetData(mediaDataC)), C.GetDataLen(mediaDataC)),
IsFinish: int(C.IsMediaDataFinish(mediaDataC)) == 1,
}, nil
}
// GetContentFromSlice 从切片内获取内容
func (s *Client) GetContentFromSlice(slice *C.struct_Slice_t) []byte {
return C.GoBytes(unsafe.Pointer(C.GetContentFromSlice(slice)), C.GetSliceLen(slice))
}
@@ -0,0 +1,20 @@
//go:build !linux || !cgo || !msgaudit
// +build !linux !cgo !msgaudit
// Package msgaudit for unsupport platform
package msgaudit
import (
"fmt"
"github.com/silenceper/wechat/v2/work/config"
)
// Client 会话存档
type Client struct {
}
// NewClient new
func NewClient(cfg *config.Config) (*Client, error) {
return nil, fmt.Errorf("会话存档功能目前只支持Linux平台运行,并且打开设置CGO_ENABLED=1")
}
+8
View File
@@ -0,0 +1,8 @@
package msgaudit
// Config 会话存档初始化参数
type Config struct {
CorpID string // 调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
CorpSecret string // 聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
RasPrivateKey string // 消息加密私钥,可以在企业微信管理端--管理工具--消息加密公钥查看对用公钥,私钥一般由自己保存
}
+76
View File
@@ -0,0 +1,76 @@
package msgaudit
import (
"fmt"
)
//返回码 错误说明
//10000 参数错误,请求参数错误
//10001 网络错误,网络请求错误
//10002 数据解析失败
//10003 系统失败
//10004 密钥错误导致加密失败
//10005 fileid错误
//10006 解密失败
//10007 找不到消息加密版本的私钥,需要重新传入私钥对
//10008 解析encrypt_key出错
//10009 ip非法
//10010 数据过期
//10011 证书错误
const (
SDKErrMsg = "sdk failed"
SDKParamsErrMsg = "参数错误,请求参数错误"
SDKNetworkErrMsg = "网络错误,网络请求错误"
SDKParseErrMsg = "数据解析失败"
SDKSystemErrMsg = "系统失败"
SDKSecretErrMsg = "密钥错误导致加密失败"
SDKFileIDErrMsg = "fileid错误"
SDKDecryptErrMsg = "解密失败"
SDKSecretMissErrMsg = "找不到消息加密版本的私钥,需要重新传入私钥对"
SDKEncryptKeyErrMsg = "解析encrypt_key出错"
SDKIPNotWhiteListErrMsg = "ip非法"
SDKDataExpiredErrMsg = "数据过期"
SDKTokenExpiredErrMsg = "证书过期"
)
// Error 错误
type Error struct {
ErrCode int `json:"errcode,omitempty"`
ErrMsg string `json:"errmsg,omitempty"`
}
func (e Error) Error() string {
return fmt.Sprintf("%d:%s", e.ErrCode, e.ErrMsg)
}
// NewSDKErr 初始化新的SDK错误
func NewSDKErr(code int) Error {
msg := ""
switch code {
case 10000:
msg = SDKParamsErrMsg
case 10001:
msg = SDKNetworkErrMsg
case 10002:
msg = SDKParseErrMsg
case 10003:
msg = SDKSystemErrMsg
case 10004:
msg = SDKSecretErrMsg
case 10005:
msg = SDKFileIDErrMsg
case 10006:
msg = SDKDecryptErrMsg
case 10007:
msg = SDKSecretMissErrMsg
case 10008:
msg = SDKEncryptKeyErrMsg
case 10009:
msg = SDKIPNotWhiteListErrMsg
case 10010:
msg = SDKDataExpiredErrMsg
case 10011:
msg = SDKTokenExpiredErrMsg
}
return Error{ErrCode: code, ErrMsg: msg}
}
+8
View File
@@ -0,0 +1,8 @@
package msgaudit
// MediaData 媒体文件数据
type MediaData struct {
OutIndexBuf string `json:"outindexbuf,omitempty"`
IsFinish bool `json:"is_finish,omitempty"`
Data []byte `json:"data,omitempty"`
}
+352
View File
@@ -0,0 +1,352 @@
package msgaudit
// BaseMessage 基础消息
type BaseMessage struct {
MsgID string `json:"msgid,omitempty"` // 消息id,消息的唯一标识,企业可以使用此字段进行消息去重。
Action string `json:"action,omitempty"` // 消息动作,目前有send(发送消息)/recall(撤回消息)/switch(切换企业日志)三种类型。
From string `json:"from,omitempty"` // 消息发送方id。同一企业内容为userid,非相同企业为external_userid。消息如果是机器人发出,也为external_userid。
ToList []string `json:"tolist,omitempty"` // 消息接收方列表,可能是多个,同一个企业内容为userid,非相同企业为external_userid。
RoomID string `json:"roomid,omitempty"` // 群聊消息的群id。如果是单聊则为空。
MsgTime int64 `json:"msgtime,omitempty"` // 消息发送时间戳,utc时间,ms单位。
MsgType string `json:"msgtype,omitempty"` // 文本消息为:text。
}
// TextMessage 文本消息
type TextMessage struct {
BaseMessage
Text struct {
Content string `json:"content,omitempty"` // 消息内容。
} `json:"text,omitempty"`
}
// ImageMessage 图片消息
type ImageMessage struct {
BaseMessage
Image struct {
SdkFileID string `json:"sdkfileid,omitempty"` // 媒体资源的id信息。
Md5Sum string `json:"md5sum,omitempty"` // 图片资源的md5值,供进行校验。
FileSize uint32 `json:"filesize,omitempty"` // 图片资源的文件大小。
} `json:"image,omitempty"`
}
// RevokeMessage 撤回消息
type RevokeMessage struct {
BaseMessage
Revoke struct {
PreMsgID string `json:"pre_msgid,omitempty"` // 标识撤回的原消息的msgid
} `json:"revoke,omitempty"`
}
// AgreeMessage 同意会话聊天内容
type AgreeMessage struct {
BaseMessage
Agree struct {
UserID string `json:"userid,omitempty"` // 同意/不同意协议者的userid,外部企业默认为external_userid。
AgreeTime int64 `json:"agree_time,omitempty"` // 同意/不同意协议的时间,utc时间,ms单位。
} `json:"agree,omitempty"`
}
// VoiceMessage 语音消息
type VoiceMessage struct {
BaseMessage
Voice struct {
SdkFileID string `json:"sdkfileid,omitempty"` // 媒体资源的id信息。
VoiceSize uint32 `json:"voice_size,omitempty"` // 语音消息大小。
PlayLength uint32 `json:"play_length,omitempty"` // 播放长度。
Md5Sum string `json:"md5sum,omitempty"` // 图片资源的md5值,供进行校验。
} `json:"voice,omitempty"`
}
// VideoMessage 视频消息
type VideoMessage struct {
BaseMessage
Video struct {
SdkFileID string `json:"sdkfileid,omitempty"` // 媒体资源的id信息。
FileSize uint32 `json:"filesize,omitempty"` // 图片资源的文件大小。
PlayLength uint32 `json:"play_length,omitempty"` // 播放长度。
Md5Sum string `json:"md5sum,omitempty"` // 图片资源的md5值,供进行校验。
} `json:"video,omitempty"`
}
// CardMessage 名片消息
type CardMessage struct {
BaseMessage
Card struct {
CorpName string `json:"corpname,omitempty"` // 名片所有者所在的公司名称。
UserID string `json:"userid,omitempty"` // 名片所有者的id,同一公司是userid,不同公司是external_userid
} `json:"card,omitempty"`
}
// LocationMessage 位置消息
type LocationMessage struct {
BaseMessage
Location struct {
Lng float64 `json:"longitude,omitempty"` // 经度,单位double
Lat float64 `json:"latitude,omitempty"` // 纬度,单位double
Address string `json:"address,omitempty"` // 地址信息
Title string `json:"title,omitempty"` // 位置信息的title。
Zoom uint32 `json:"zoom,omitempty"` // 缩放比例。
} `json:"location,omitempty"`
}
// EmotionMessage 表情消息
type EmotionMessage struct {
BaseMessage
Emotion struct {
Type uint32 `json:"type,omitempty"` // 表情类型,png或者gif.1表示gif 2表示png。
Width uint32 `json:"width,omitempty"` // 表情图片宽度。
Height uint32 `json:"height,omitempty"` // 表情图片高度。
ImageSize uint32 `json:"imagesize,omitempty"` // 资源的文件大小。
SdkFileID string `json:"sdkfileid,omitempty"` // 媒体资源的id信息。
Md5Sum string `json:"md5sum,omitempty"` // 图片资源的md5值,供进行校验。
} `json:"emotion,omitempty"`
}
// FileMessage 文件消息
type FileMessage struct {
BaseMessage
File struct {
FileName string `json:"filename,omitempty"` // 文件名称。
FileExt string `json:"fileext,omitempty"` // 文件类型后缀。
SdkFileID string `json:"sdkfileid,omitempty"` // 媒体资源的id信息。
FileSize uint32 `json:"filesize,omitempty"` // 文件大小。
Md5Sum string `json:"md5sum,omitempty"` // 资源的md5值,供进行校验。
} `json:"file,omitempty"`
}
// LinkMessage 链接消息
type LinkMessage struct {
BaseMessage
Link struct {
Title string `json:"title,omitempty"` // 消息标题。
Desc string `json:"description,omitempty"` // 消息描述。
LinkURL string `json:"link_url,omitempty"` // 链接url地址
ImageURL string `json:"image_url,omitempty"` // 链接图片url。
} `json:"link,omitempty"`
}
// WeappMessage 小程序消息
type WeappMessage struct {
BaseMessage
WeApp struct {
Title string `json:"title,omitempty"` // 消息标题。
Desc string `json:"description,omitempty"` // 消息描述。
Username string `json:"username,omitempty"` // 用户名称。
DisplayName string `json:"displayname,omitempty"` // 小程序名称
} `json:"weapp,omitempty"`
}
// ChatRecordMessage 会话记录消息
type ChatRecordMessage struct {
BaseMessage
ChatRecord struct {
Title string `json:"title,omitempty"` // 聊天记录标题
Item []ChatRecord `json:"item,omitempty"` // 消息记录内的消息内容,批量数据
} `json:"chatrecord,omitempty"`
}
// TodoMessage 待办消息
type TodoMessage struct {
BaseMessage
Todo struct {
Title string `json:"title,omitempty"` // 代办的来源文本
Content string `json:"content,omitempty"` // 代办的具体内容
} `json:"todo,omitempty"`
}
// VoteMessage 投票消息
type VoteMessage struct {
BaseMessage
VoteTitle string `json:"votetitle,omitempty"` // 投票主题。
VoteItem []string `json:"voteitem,omitempty"` // 投票选项,可能多个内容。
VoteType uint32 `json:"votetype,omitempty"` // 投票类型.101发起投票、102参与投票。
VoteID string `json:"voteid,omitempty"` // 投票id,方便将参与投票消息与发起投票消息进行前后对照。
}
// CollectMessage 填表消息
type CollectMessage struct {
BaseMessage
Collect struct {
RoomName string `json:"room_name,omitempty"` // 填表消息所在的群名称。
Creator string `json:"creator,omitempty"` // 创建者在群中的名字
CreateTime string `json:"create_time,omitempty"` // 创建的时间
Details []CollectDetails `json:"details,omitempty"` // 表内容
} `json:"collect,omitempty"`
}
// RedpacketMessage 红包消息
type RedpacketMessage struct {
BaseMessage
RedPacket struct {
Type uint32 `json:"type,omitempty"` // 红包消息类型。1 普通红包、2 拼手气群红包、3 激励群红包。
Wish string `json:"wish,omitempty"` // 红包祝福语
TotalCnt uint32 `json:"totalcnt,omitempty"` // 红包总个数
TotalAmount uint32 `json:"totalamount,omitempty"` // 红包总金额。单位为分。
} `json:"redpacket,omitempty"`
}
// MeetingMessage 会议邀请消息
type MeetingMessage struct {
BaseMessage
Meeting struct {
Topic string `json:"topic,omitempty"` // 会议主题
StartTime int64 `json:"starttime,omitempty"` // 会议开始时间。Utc时间
EndTime int64 `json:"endtime,omitempty"` // 会议结束时间。Utc时间
Address string `json:"address,omitempty"` // 会议地址
Remarks string `json:"remarks,omitempty"` // 会议备注
MeetingType uint32 `json:"meetingtype,omitempty"` // 会议消息类型。101发起会议邀请消息、102处理会议邀请消息
MeetingID uint64 `json:"meetingid,omitempty"` // 会议id。方便将发起、处理消息进行对照
Status uint32 `json:"status,omitempty"` // 会议邀请处理状态。1 参加会议、2 拒绝会议、3 待定、4 未被邀请、5 会议已取消、6 会议已过期、7 不在房间内。
} `json:"meeting,omitempty"`
}
// DocMessage 在线文档消息
type DocMessage struct {
BaseMessage
Doc struct {
Title string `json:"title,omitempty"` // 在线文档名称
LinkURL string `json:"link_url,omitempty"` // 在线文档链接
DocCreator string `json:"doc_creator,omitempty"` // 在线文档创建者。本企业成员创建为userid;外部企业成员创建为external_userid
} `json:"doc,omitempty"`
}
// MarkdownMessage MarkDown消息
type MarkdownMessage struct {
BaseMessage
Info struct {
Content string `json:"content,omitempty"` // markdown消息内容,目前为机器人发出的消息
} `json:"info,omitempty"`
}
// NewsMessage 图文消息
type NewsMessage struct {
BaseMessage
Info struct {
Item []News `json:"item,omitempty"` // 图文消息数组
} `json:"info,omitempty"` // 图文消息的内容
}
// CalendarMessage 日程消息
type CalendarMessage struct {
BaseMessage
Calendar struct {
Title string `json:"title,omitempty"` // 日程主题
CreatorName string `json:"creatorname,omitempty"` // 日程组织者
AttendeeName []string `json:"attendeename,omitempty"` // 日程参与人。数组,内容为String类型
StartTime int64 `json:"starttime,omitempty"` // 日程开始时间。Utc时间,单位秒
EndTime int64 `json:"endtime,omitempty"` // 日程结束时间。Utc时间,单位秒
Place string `json:"place,omitempty"` // 日程地点
Remarks string `json:"remarks,omitempty"` // 日程备注
} `json:"calendar,omitempty"`
}
// MixedMessage 混合消息
type MixedMessage struct {
BaseMessage
Mixed struct {
Item []MixedMsg `json:"item,omitempty"`
} `json:"mixed,omitempty"` // 消息内容。可包含图片、文字、表情等多种消息。Object类型
}
// MeetingVoiceCallMessage 音频存档消息
type MeetingVoiceCallMessage struct {
BaseMessage
VoiceID string `json:"voiceid,omitempty"` // 音频id
MeetingVoiceCall *MeetingVoiceCall `json:"meeting_voice_call,omitempty"` // 音频消息内容。包括结束时间、fileid,可能包括多个demofiledata、sharescreendata消息,demofiledata表示文档共享信息,sharescreendata表示屏幕共享信息。Object类型
}
// VoipDocShareMessage 音频共享消息
type VoipDocShareMessage struct {
BaseMessage
VoipID string `json:"voipid,omitempty"` // 音频id
VoipDocShare *VoipDocShare `json:"voip_doc_share,omitempty"` // 共享文档消息内容。包括filename、md5sum、filesize、sdkfileid字段。Object类型
}
// ExternalRedPacketMessage 互通小红包消息
type ExternalRedPacketMessage struct {
BaseMessage
RedPacket struct {
Type int32 `json:"type,omitempty"` // 红包消息类型。1 普通红包、2 拼手气群红包。Uint32类型
Wish int32 `json:"wish,omitempty"` // 红包祝福语。String类型
TotalCnt int32 `json:"totalcnt,omitempty"` // 红包总个数。Uint32类型
TotalAmount int32 `json:"totalamount,omitempty"` // 红包消息类型。1 普通红包、2 拼手气群红包。Uint32类型
} `json:"redpacket,omitempty"`
}
// SphFeedMessage 视频号消息
type SphFeedMessage struct {
BaseMessage
SphFeed struct {
FeedType string `json:"feed_type,omitempty"` // 视频号消息类型
SphName string `json:"sph_name,omitempty"` // 视频号账号名称
FeedDesc uint64 `json:"feed_desc,omitempty"` // 视频号账号名称
}
}
// SwitchMessage 企业切换日志
type SwitchMessage struct {
MsgID string `json:"msgid,omitempty"` // 消息id,消息的唯一标识,企业可以使用此字段进行消息去重
Action string `json:"action,omitempty"` // 消息动作,切换企业为switch
Time int64 `json:"time,omitempty"` // 消息发送时间戳,utc时间,ms单位。
User string `json:"user,omitempty"` // 具体为切换企业的成员的userid。
}
// ChatRecord 会话记录消息
type ChatRecord struct {
Type string `json:"type,omitempty"` // 每条聊天记录的具体消息类型:ChatRecordText/ ChatRecordFile/ ChatRecordImage/ ChatRecordVideo/ ChatRecordLink/ ChatRecordLocation/ ChatRecordMixed ….
Content string `json:"content,omitempty"` // 消息内容。Json串,内容为对应类型的json
MsgTime int64 `json:"msgtime,omitempty"` // 消息时间,utc时间,ms单位。
FromChatroom bool `json:"from_chatroom,omitempty"` // 是否来自群会话。
}
// CollectDetails 填表消息
type CollectDetails struct {
ID uint64 `json:"id,omitempty"` // 表项id
Ques string `json:"ques,omitempty"` // 表项名称
Type string `json:"type,omitempty"` // 表项类型,有Text(文本),Number(数字),Date(日期),Time(时间)
}
// News 图文消息
type News struct {
Title string `json:"title,omitempty"` // 图文消息标题
Desc string `json:"description,omitempty"` // 图文消息描述
URL string `json:"url,omitempty"` // 图文消息点击跳转地址
PicURL string `json:"picurl,omitempty"` // 图文消息配图的url
}
// MixedMsg 混合消息
type MixedMsg struct {
Type string `json:"type,omitempty"`
Content string `json:"content,omitempty"`
}
// MeetingVoiceCall 音频存档消息
type MeetingVoiceCall struct {
EndTime int64 `json:"endtime,omitempty"` // 音频结束时间
SdkFileID string `json:"sdkfileid,omitempty"` // 音频媒体下载的id
DemoFileData []DemoFileData `json:"demofiledata,omitempty"` // 文档分享对象,Object类型
ShareScreenData []ShareScreenData `json:"sharescreendata,omitempty"` // 屏幕共享对象,Object类型
}
// DemoFileData 文档共享消息
type DemoFileData struct {
FileName string `json:"filename,omitempty"` // 文档共享名称
DemoOperator string `json:"demooperator,omitempty"` // 文档共享操作用户的id
StartTime int64 `json:"starttime,omitempty"` // 文档共享开始时间
EndTime int64 `json:"endtime,omitempty"` // 文档共享结束时间
}
// ShareScreenData 屏幕共享信息
type ShareScreenData struct {
Share string `json:"share,omitempty"` // 屏幕共享用户的id
StartTime int64 `json:"starttime,omitempty"` // 屏幕共享开始时间
EndTime int64 `json:"endtime,omitempty"` // 屏幕共享结束时间
}
// VoipDocShare 音频共享文档消息
type VoipDocShare struct {
FileName string `json:"filename,omitempty"` // 文档共享文件名称
Md5Sum string `json:"md5sum,omitempty"` // 共享文件的md5值
FileSize uint64 `json:"filesize,omitempty"` // 共享文件的大小
SdkFileID string `json:"sdkfileid,omitempty"` // 共享文件的sdkfile,通过此字段进行媒体数据下载
}