client_linux.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. //go:build linux && cgo && msgaudit
  2. // +build linux,cgo,msgaudit
  3. // Package msgaudit only for linux
  4. package msgaudit
  5. // #cgo LDFLAGS: -L${SRCDIR}/lib -lWeWorkFinanceSdk_C
  6. // #cgo CFLAGS: -I ./lib/
  7. // #include <stdlib.h>
  8. // #include "WeWorkFinanceSdk_C.h"
  9. import "C"
  10. import (
  11. "encoding/json"
  12. "unsafe"
  13. "github.com/silenceper/wechat/v2/util"
  14. "github.com/silenceper/wechat/v2/work/config"
  15. )
  16. // Client 会话存档
  17. type Client struct {
  18. ptr *C.WeWorkFinanceSdk_t
  19. privateKey string
  20. }
  21. // NewClient 初始会话会话存档实例
  22. /**
  23. * 初始化函数
  24. * Return 值=0 表示该 API 调用成功
  25. *
  26. * @param [in] sdk NewSdk 返回的 sdk 指针
  27. * @param [in] corpid 调用企业的企业 id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
  28. * @param [in] secret 聊天内容存档的 Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
  29. * @param [in] privateKey 消息加密私钥,可以在企业微信管理端--管理工具--消息加密公钥查看对用公钥,私钥一般由自己保存
  30. *
  31. *
  32. * @return 返回是否初始化成功
  33. * 0 - 成功
  34. * !=0 - 失败
  35. */
  36. func NewClient(cfg *config.Config) (*Client, error) {
  37. ptr := C.NewSdk()
  38. corpIDC := C.CString(cfg.CorpID)
  39. corpSecretC := C.CString(cfg.CorpSecret)
  40. defer func() {
  41. C.free(unsafe.Pointer(corpIDC))
  42. C.free(unsafe.Pointer(corpSecretC))
  43. }()
  44. retC := C.Init(ptr, corpIDC, corpSecretC)
  45. ret := int(retC)
  46. if ret != 0 {
  47. return nil, NewSDKErr(ret)
  48. }
  49. return &Client{
  50. ptr: ptr,
  51. privateKey: cfg.RasPrivateKey,
  52. }, nil
  53. }
  54. // Free 释放 SDK 实例是可调用该方法释放内存
  55. func (s *Client) Free() {
  56. if s.ptr == nil {
  57. return
  58. }
  59. C.DestroySdk(s.ptr)
  60. s.ptr = nil
  61. }
  62. // GetChatData 拉取聊天记录函数
  63. /**
  64. * 拉取聊天记录函数
  65. *
  66. *
  67. * @param [in] seq 从指定的 seq 开始拉取消息,注意的是返回的消息从 seq+1 开始返回,seq 为之前接口返回的最大 seq 值。首次使用请使用 seq:0
  68. * @param [in] limit 一次拉取的消息条数,最大值 1000 条,超过 1000 条会返回错误
  69. * @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
  70. * @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
  71. * @param [in] timeout 超时时间,单位秒
  72. * @return chatDatas 返回本次拉取消息的数据,slice 结构体。内容包括 errcode/errmsg,以及每条消息内容。示例如下:
  73. {"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"}]}
  74. */
  75. func (s *Client) GetChatData(seq uint64, limit uint64, proxy string, passwd string, timeout int) ([]ChatData, error) {
  76. proxyC := C.CString(proxy)
  77. passwdC := C.CString(passwd)
  78. chatSlice := C.NewSlice()
  79. defer func() {
  80. C.free(unsafe.Pointer(proxyC))
  81. C.free(unsafe.Pointer(passwdC))
  82. C.FreeSlice(chatSlice)
  83. }()
  84. if s.ptr == nil {
  85. return nil, NewSDKErr(10002)
  86. }
  87. retC := C.GetChatData(s.ptr, C.ulonglong(seq), C.uint(limit), proxyC, passwdC, C.int(timeout), chatSlice)
  88. ret := int(retC)
  89. if ret != 0 {
  90. return nil, NewSDKErr(ret)
  91. }
  92. buf := s.GetContentFromSlice(chatSlice)
  93. var data ChatDataResponse
  94. err := json.Unmarshal(buf, &data)
  95. if err != nil {
  96. return nil, err
  97. }
  98. if data.ErrCode != 0 {
  99. return nil, data.Error
  100. }
  101. return data.ChatDataList, nil
  102. }
  103. // GetRawChatData 拉取聊天记录函数
  104. /**
  105. * 拉取聊天记录函数
  106. *
  107. *
  108. * @param [in] seq 从指定的 seq 开始拉取消息,注意的是返回的消息从 seq+1 开始返回,seq 为之前接口返回的最大 seq 值。首次使用请使用 seq:0
  109. * @param [in] limit 一次拉取的消息条数,最大值 1000 条,超过 1000 条会返回错误
  110. * @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
  111. * @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
  112. * @param [in] timeout 超时时间,单位秒
  113. * @return chatDatas 返回本次拉取消息的数据,slice 结构体。内容包括 errcode/errmsg,以及每条消息内容。示例如下:
  114. {"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"}]}
  115. */
  116. func (s *Client) GetRawChatData(seq uint64, limit uint64, proxy string, passwd string, timeout int) (ChatDataResponse, error) {
  117. proxyC := C.CString(proxy)
  118. passwdC := C.CString(passwd)
  119. chatSlice := C.NewSlice()
  120. defer func() {
  121. C.free(unsafe.Pointer(proxyC))
  122. C.free(unsafe.Pointer(passwdC))
  123. C.FreeSlice(chatSlice)
  124. }()
  125. if s.ptr == nil {
  126. return ChatDataResponse{}, NewSDKErr(10002)
  127. }
  128. retC := C.GetChatData(s.ptr, C.ulonglong(seq), C.uint(limit), proxyC, passwdC, C.int(timeout), chatSlice)
  129. ret := int(retC)
  130. if ret != 0 {
  131. return ChatDataResponse{}, NewSDKErr(ret)
  132. }
  133. buf := s.GetContentFromSlice(chatSlice)
  134. var data ChatDataResponse
  135. err := json.Unmarshal(buf, &data)
  136. return data, err
  137. }
  138. // DecryptData 解析密文。企业微信自有解密内容
  139. /**
  140. * @brief 解析密文。企业微信自有解密内容
  141. * @param [in] encrypt_key, getchatdata 返回的 encrypt_random_key,使用企业自持对应版本秘钥 RSA 解密后的内容
  142. * @param [in] encrypt_msg, getchatdata 返回的 encrypt_chat_msg
  143. * @param [out] msg, 解密的消息明文
  144. * @return 返回是否调用成功
  145. * 0 - 成功
  146. * !=0 - 失败
  147. */
  148. func (s *Client) DecryptData(encryptRandomKey string, encryptMsg string) (msg ChatMessage, err error) {
  149. encryptKey, err := util.RSADecryptBase64(s.privateKey, encryptRandomKey)
  150. if err != nil {
  151. return msg, err
  152. }
  153. encryptKeyC := C.CString(string(encryptKey))
  154. encryptMsgC := C.CString(encryptMsg)
  155. msgSlice := C.NewSlice()
  156. defer func() {
  157. C.free(unsafe.Pointer(encryptKeyC))
  158. C.free(unsafe.Pointer(encryptMsgC))
  159. C.FreeSlice(msgSlice)
  160. }()
  161. retC := C.DecryptData(encryptKeyC, encryptMsgC, msgSlice)
  162. ret := int(retC)
  163. if ret != 0 {
  164. return msg, NewSDKErr(ret)
  165. }
  166. buf := s.GetContentFromSlice(msgSlice)
  167. // handle illegal escape character in text
  168. for i := 0; i < len(buf); {
  169. if buf[i] < 0x20 {
  170. buf = append(buf[:i], buf[i+1:]...)
  171. continue
  172. }
  173. i++
  174. }
  175. var baseMessage BaseMessage
  176. err = json.Unmarshal(buf, &baseMessage)
  177. if err != nil {
  178. return msg, err
  179. }
  180. msg.ID = baseMessage.MsgID
  181. msg.From = baseMessage.From
  182. msg.ToList = baseMessage.ToList
  183. msg.Action = baseMessage.Action
  184. msg.Type = baseMessage.MsgType
  185. msg.originData = buf
  186. return msg, err
  187. }
  188. // GetMediaData 拉取媒体消息函数
  189. /**
  190. * 拉取媒体消息函数
  191. * Return 值=0 表示该 API 调用成功
  192. *
  193. *
  194. * @param [in] sdk NewSdk 返回的 sdk 指针
  195. * @param [in] sdkFileid 从 GetChatData 返回的聊天消息中,媒体消息包括的 sdkfileid
  196. * @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
  197. * @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
  198. * @param [in] indexbuf 媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,默认拉取 512k,后续每次调用只需要将上次调用返回的 outindexbuf 填入即可。
  199. * @param [in] timeout 超时时间,单位秒
  200. * @param [out] media_data 返回本次拉取的媒体数据.MediaData 结构体。内容包括 data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记)
  201. *
  202. * @return 返回是否调用成功
  203. * 0 - 成功
  204. * !=0 - 失败
  205. */
  206. func (s *Client) GetMediaData(indexBuf string, sdkFileID string, proxy string, passwd string, timeout int) (*MediaData, error) {
  207. indexBufC := C.CString(indexBuf)
  208. sdkFileIDC := C.CString(sdkFileID)
  209. proxyC := C.CString(proxy)
  210. passwdC := C.CString(passwd)
  211. mediaDataC := C.NewMediaData()
  212. defer func() {
  213. C.free(unsafe.Pointer(indexBufC))
  214. C.free(unsafe.Pointer(sdkFileIDC))
  215. C.free(unsafe.Pointer(proxyC))
  216. C.free(unsafe.Pointer(passwdC))
  217. C.FreeMediaData(mediaDataC)
  218. }()
  219. if s.ptr == nil {
  220. return nil, NewSDKErr(10002)
  221. }
  222. retC := C.GetMediaData(s.ptr, indexBufC, sdkFileIDC, proxyC, passwdC, C.int(timeout), mediaDataC)
  223. ret := int(retC)
  224. if ret != 0 {
  225. return nil, NewSDKErr(ret)
  226. }
  227. return &MediaData{
  228. OutIndexBuf: C.GoString(C.GetOutIndexBuf(mediaDataC)),
  229. Data: C.GoBytes(unsafe.Pointer(C.GetData(mediaDataC)), C.GetDataLen(mediaDataC)),
  230. IsFinish: int(C.IsMediaDataFinish(mediaDataC)) == 1,
  231. }, nil
  232. }
  233. // GetContentFromSlice 从切片内获取内容
  234. func (s *Client) GetContentFromSlice(slice *C.struct_Slice_t) []byte {
  235. return C.GoBytes(unsafe.Pointer(C.GetContentFromSlice(slice)), C.GetSliceLen(slice))
  236. }