C#.NET企业微信会话内容存档

一、项目结果图

二、通过官方提供的C++版的ddl来实现,两个关键的类

1.Finance 类,调用官方提供的C++版的ddl 类

using System;
using System.Runtime.InteropServices;

namespace WeWork.Service
{
    public static class Finance
    {
        /// <summary>
        /// 企业微信会话存档DDL
        /// </summary>
        private const string DllName = "Lib\\WeWorkFinanceSdk.dll";

        /// <summary>
        /// 获取sdk对象,首次使用初始化
        /// </summary>
        /// <returns></returns>
        [DllImport(DllName)]
        public extern static IntPtr NewSdk();

        /// <summary>
        /// 初始化函数
        /// Return值=0表示该API调用成功
        /// </summary>
        /// <param name="sdk">NewSdk返回的sdk指针</param>
        /// <param name="corpid">调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看</param>
        /// <param name="secret">聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看</param>
        /// <returns>返回是否初始化成功 0 - 成功  !=0 - 失败</returns>
        [DllImport(DllName)]
        public extern static int Init(IntPtr sdk, String corpid, String secret);

        /// <summary>
        /// 拉取聊天记录函数
        /// Return值=0表示该API调用成功
        /// </summary>
        /// <param name="sdk">NewSdk返回的sdk指针</param>
        /// <param name="seq">从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0</param>
        /// <param name="limit">一次拉取的消息条数,最大值1000条,超过1000条会返回错误</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间,单位秒</param>
        /// <param name="chatData">返回本次拉取消息的数据,slice结构体.内容包括errcode/errmsg,以及每条消息内容。</param>
        /// <returns>返回是否初始化成功 0 - 成功  !=0 - 失败</returns>
        [DllImport(DllName)]
        public extern static int GetChatData(IntPtr sdk, long seq, long limit, String proxy, String passwd, long timeout, IntPtr chatData);

        /// <summary>
        /// 拉取媒体消息函数
        /// Return值=0表示该API调用成功
        /// </summary>
        /// <param name="sdk">NewSdk返回的sdk指针</param>
        /// <param name="indexbuf">从GetChatData返回的聊天消息中,媒体消息包括的sdkfileid</param>
        /// <param name="sdkField">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081</param>
        /// <param name="proxy">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="passwd">媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,默认拉取512k,后续每次调用只需要将上次调用返回的outindexbuf填入即可。</param>
        /// <param name="timeout">超时时间,单位秒</param>
        /// <param name="mediaData">返回本次拉取的媒体数据.MediaData结构体.内容包括data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记)</param>
        /// <returns>返回是否初始化成功 0 - 成功  !=0 - 失败</returns>
        [DllImport(DllName)]
        public extern static int GetMediaData(IntPtr sdk, string indexbuf, string sdkField, string proxy, string passwd, long timeout, IntPtr mediaData);

        /// <summary>
        /// 解析密文
        /// Return值=0表示该API调用成功
        /// </summary>
        /// <param name="encrypt_key">getchatdata返回的encrypt_random_key,使用企业自持对应版本秘钥RSA解密后的内容</param>
        /// <param name="encrypt_msg">getchatdata返回的encrypt_chat_msg</param>
        /// <param name="msg">解密的消息明文</param>
        /// <returns>返回是否初始化成功 0 - 成功  !=0 - 失败</returns>
        [DllImport(DllName)]
        public extern static int DecryptData(string encrypt_key, string encrypt_msg, IntPtr msg);


        [DllImport(DllName)]
        public extern static void DestroySdk(IntPtr sdk);
        [DllImport(DllName)]
        public extern static IntPtr NewSlice();

        /// <summary>
        /// 释放slice,和NewSlice成对使用
        /// </summary>
        /// <param name="slice"></param>
        [DllImport(DllName)]
        public extern static void FreeSlice(IntPtr slice);

        /// <summary>
        /// 获取slice内容
        /// </summary>
        /// <param name="slice"></param>
        /// <returns>内容</returns>
        [DllImport(DllName)]
        // IntPtr 换成 String 就需要将下面这个注释启用
        //[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]
        public extern static IntPtr GetContentFromSlice(IntPtr slice);

        /// <summary>
        /// 获取slice内容长度
        /// </summary>
        /// <param name="slice"></param>
        /// <returns>内容长度</returns>
        [DllImport(DllName)]
        public extern static int GetSliceLen(IntPtr slice);
        [DllImport(DllName)]
        public extern static IntPtr NewMediaData();
        [DllImport(DllName)]
        public extern static void FreeMediaData(IntPtr mediaData);

        /// <summary>
        /// 获取mediadata outindex
        /// </summary>
        /// <param name="mediaData"></param>
        /// <returns>outindex</returns>
        [DllImport(DllName)]
        public extern static IntPtr GetOutIndexBuf(IntPtr mediaData);

        /// <summary>
        /// 获取mediadata data数据
        /// </summary>
        /// <param name="mediaData"></param>
        /// <returns>data</returns>
        [DllImport(DllName)]
        public extern static IntPtr GetData(IntPtr mediaData);
        [DllImport(DllName)]
        public extern static int GetIndexLen(IntPtr mediaData);
        [DllImport(DllName)]
        public extern static int GetDataLen(IntPtr mediaData);

        /// <summary>
        /// 判断mediadata是否结束
        /// </summary>
        /// <param name="mediaData"></param>
        /// <returns>1完成、0未完成</returns>
        [DllImport(DllName)]
        public extern static int IsMediaDataFinish(IntPtr mediaData);
    }
}

2.FinanceHelper 类,这个就是我们可以实例化调用方法获取会话记录内容的类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using WeWork.Common;
using WeWork.Model;

namespace WeWork.Service
{
    /// <summary>
    /// 会话内容存档
    /// 媒体消息的格式分别是什么?
    /// 图片是jpg格式、语音是amr格式、视频/音频是mp4格式、文件格式类型包括在消息体内,表情分为动图(gif)与静态图(png),在消息体内定义。
    /// </summary>
    public class FinanceHelper
    {
        /// <summary>
        /// 调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
        /// </summary>
        public string CorpId = "";
        /// <summary>
        /// 聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
        /// </summary>
        public string Secret = "";
        /// <summary>
        /// 会话加密私钥
        /// </summary>
        public Dictionary<int, string> VerKey = null;
        /// <summary>
        /// 企业微信SDK
        /// </summary>
        public IntPtr SDK = IntPtr.Zero;
        /// <summary>
        /// 每次拉去会话记录数上限为1000(主要是为了防止记录太多爆内存)
        /// </summary>
        public int MaxLimit = 1000;

        /// <summary>
        /// 初始化SDK
        /// </summary>
        /// <param name="corpid">调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看</param>
        /// <param name="secret">会话内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看</param>
        /// <param name="verKey">会话内容存档加密版本的私钥集合, 企业微信的会话内容存档,提供的解密密钥是一段文本字符串,需要将其转换为xml格式。可将文本字符串保存为.pem文件,然后通过以下地址转换: https://the-x.cn/certificate/PemToXml.aspx </param>
        public FinanceHelper(string corpid, string secret, Dictionary<int, string> verKey)
        {
            this.CorpId = corpid;
            this.Secret = secret;
            this.VerKey = verKey;

            IntPtr sdk = Finance.NewSdk();

            //企业微信 corpid,secret
            var result = Finance.Init(sdk, CorpId, Secret);
            if (result == 0)
            {
                this.SDK = sdk;
            }
            else
            {
                Log.Error("FinanceHelper", "企业微信-会话内容存档:初始化SDK阶段返回失败。");
            }
        }

        #region 同步方法
        /// <summary>
        /// 循环获取会话记录数据 以及媒体列表
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型,范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值,不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长,单位 秒</param>
        /// <returns></returns>
        public Tuple<List<IMsgBase>, List<FileData>> GetChatList(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 60)
        {
            List<IMsgBase> msgList = new List<IMsgBase>();
            List<FileData> fileList = new List<FileData>();
            DateTime startTime = DateTime.Now;
            int pullNumber = 0; //拉取的次数
            //获取聊天信息
            bool isGet = true;
            while (isGet)
            {
                var result = GetChatData(seq, limit, proxy, passwd, timeout);
                List<IMsgBase> list1 = result.Item1; //企业微信 会话内容 列表
                List<FileData> list2 = result.Item2; //媒体内容 列表

                var count = list1.Count;
                if (count > 0)
                {
                    foreach (var item in list1)
                    {
                        msgList.Add(item);
                    }
                    foreach (var item in list2)
                    {
                        fileList.Add(item);
                    }
                    if (count < MaxLimit)
                    {
                        //总数没有超过 限制拉取的数量时 说明已经拉取完成 不在循环拉取了
                        if (count >= limit)
                        {
                            seq = list1.OrderByDescending(t => t.seq).Select(q => q.seq).FirstOrDefault();
                        }
                        else
                        {
                            isGet = false;
                        }
                    }
                    else
                    {
                        isGet = false;
                    }
                }
                else
                {
                    isGet = false;
                }

                //看看 调用频率不可超过600次 / 分钟。
                pullNumber++;

                DateTime endTime = DateTime.Now;
                TimeSpan timeDifference = endTime - startTime;
                double secondsDifference = timeDifference.TotalSeconds;
                if (secondsDifference < 60)
                {
                    if (pullNumber >= 600)
                    {
                        isGet = false;
                    }
                }
            }

            return Tuple.Create(msgList, fileList);
        }

        /// <summary>
        /// 获取会话记录数据  以及媒体列表
        /// 注:获取会话记录内容不能超过5天,如果企业需要全量数据,则企业需要定期拉取聊天消息。返回的ChatDatas内容为json格式。
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型,范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值,不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长,单位 秒</param>
        /// <returns></returns>
        public Tuple<List<IMsgBase>, List<FileData>> GetChatData(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 300)
        {
            List<IMsgBase> msgList = new List<IMsgBase>();
            List<FileData> fileList = new List<FileData>();
            var slice = Finance.NewSlice();
            try
            {
                var result = Finance.GetChatData(this.SDK, seq, limit, proxy, passwd, timeout, slice);
                if (result == 0)
                {
                    var content = GetContentFromSlice(slice);
                    var encryptChatData = JsonHelper.JsonToT<GetChatData>(content);
                    if (encryptChatData.errcode == 0)
                    {
                        foreach (var encryptChat in encryptChatData.chatdata)
                        {
                            var msgseq = encryptChat.seq;
                            string fileName = "";
                            string md5Sum = "";
                            string sdkFileId = "";
                            IMsgBase msgModel = null;
                            FileData fileData = null;

                            //根据加密此条消息使用的公钥版本号匹配私钥
                            var flag = VerKey.TryGetValue(encryptChat.publickey_ver, out var privateKey);
                            if (flag)
                            {
                                if (!string.IsNullOrWhiteSpace(privateKey))
                                {
                                    var key = Decrypt(privateKey, encryptChat.encrypt_random_key);
                                    if (!string.IsNullOrWhiteSpace(key))
                                    {
                                        var msgSlice = Finance.NewSlice();
                                        var deRes = Finance.DecryptData(key, encryptChat.encrypt_chat_msg, msgSlice);
                                        var chatJsonStr = GetContentFromSlice(msgSlice);

                                        Finance.FreeSlice(msgSlice);

                                        if (!string.IsNullOrWhiteSpace(chatJsonStr))
                                        {
                                            msgModel = JsonHelper.JsonToT<MsgBase>(chatJsonStr);
                                            #region 
                                            var action = msgModel.action;//消息动作,目前有send(发送消息)/recall(撤回消息)/switch(切换企业日志)三种类型。
                                            switch (action)
                                            {
                                                case "switch"://切换企业日志
                                                    msgModel = JsonHelper.JsonToT<ChatSwitch>(chatJsonStr);
                                                    break;
                                                case "recall"://撤回消息
                                                    msgModel = JsonHelper.JsonToT<ChatRevoke>(chatJsonStr);
                                                    break;
                                                case "send"://发送消息
                                                    var sendChat = JsonHelper.JsonToT<MsgChatBase>(chatJsonStr);
                                                    var msgtype = sendChat.msgtype;
                                                    switch (msgtype)
                                                    {
                                                        case MsgType.Text://文本
                                                            msgModel = JsonHelper.JsonToT<ChatText>(chatJsonStr);
                                                            break;
                                                        case MsgType.Image://图片
                                                            {
                                                                var chatImage = JsonHelper.JsonToT<ChatImage>(chatJsonStr);
                                                                msgModel = chatImage;
                                                                fileName = $"{Guid.NewGuid()}.jpg";
                                                                md5Sum = chatImage.image.md5sum;
                                                                sdkFileId = chatImage.image.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "image",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatImage.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.Agree://同意会话聊天内容
                                                            msgModel = JsonHelper.JsonToT<ChatAgree>(chatJsonStr);
                                                            break;
                                                        case MsgType.Disagree://不同意会话聊天内容
                                                            msgModel = JsonHelper.JsonToT<ChatDisAgree>(chatJsonStr);
                                                            break;
                                                        case MsgType.Voice://语音
                                                            {
                                                                var chatVoice = JsonHelper.JsonToT<ChatVoice>(chatJsonStr);
                                                                msgModel = chatVoice;
                                                                fileName = $"{Guid.NewGuid()}.amr";
                                                                md5Sum = chatVoice.voice.md5sum;
                                                                sdkFileId = chatVoice.voice.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "voice",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatVoice.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.Video://视频
                                                            {
                                                                var chatVideo = JsonHelper.JsonToT<ChatVideo>(chatJsonStr);
                                                                msgModel = chatVideo;
                                                                fileName = $"{Guid.NewGuid()}.mp4";
                                                                md5Sum = chatVideo.video.md5sum;
                                                                sdkFileId = chatVideo.video.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "video",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatVideo.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.Card://名片
                                                            msgModel = JsonHelper.JsonToT<ChatCard>(chatJsonStr);
                                                            break;
                                                        case MsgType.Location://位置
                                                            msgModel = JsonHelper.JsonToT<ChatLocation>(chatJsonStr);
                                                            break;
                                                        case MsgType.Emotion://表情
                                                            {
                                                                var chatEmotion = JsonHelper.JsonToT<ChatEmotion>(chatJsonStr);
                                                                msgModel = chatEmotion;
                                                                switch (chatEmotion.emotion.type)
                                                                {
                                                                    case 1:
                                                                        fileName = $"{Guid.NewGuid()}.gif";
                                                                        break;
                                                                    case 2:
                                                                        fileName = $"{Guid.NewGuid()}.png";
                                                                        break;
                                                                }
                                                                md5Sum = chatEmotion.emotion.md5sum;
                                                                sdkFileId = chatEmotion.emotion.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "image",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatEmotion.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.File://文件
                                                            {
                                                                var chatFile = JsonHelper.JsonToT<ChatFile>(chatJsonStr);
                                                                msgModel = chatFile;
                                                                fileName = $"{Guid.NewGuid()}.{chatFile.file.fileext}";
                                                                md5Sum = chatFile.file.md5sum;
                                                                sdkFileId = chatFile.file.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "file",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatFile.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.Link://链接
                                                            msgModel = JsonHelper.JsonToT<ChatLink>(chatJsonStr);
                                                            break;
                                                        case MsgType.WeApp://小程序消息
                                                            msgModel = JsonHelper.JsonToT<ChatWeapp>(chatJsonStr);
                                                            break;
                                                        case MsgType.ChatRecord://会话记录消息
                                                            msgModel = JsonHelper.JsonToT<ChatRecord>(chatJsonStr);
                                                            break;
                                                        case MsgType.Todo://待办消息
                                                            msgModel = JsonHelper.JsonToT<ChatTodo>(chatJsonStr);
                                                            break;
                                                        case MsgType.Vote://投票消息
                                                            msgModel = JsonHelper.JsonToT<ChatVote>(chatJsonStr);
                                                            break;
                                                        case MsgType.Collect://填表消息
                                                            msgModel = JsonHelper.JsonToT<ChatCollect>(chatJsonStr);
                                                            break;
                                                        case MsgType.RedPacket://红包消息
                                                            msgModel = JsonHelper.JsonToT<ChatRedPacket>(chatJsonStr);
                                                            break;
                                                        case MsgType.Meeting://会议邀请消息
                                                            msgModel = JsonHelper.JsonToT<ChatMeeting>(chatJsonStr);
                                                            break;
                                                        case MsgType.DocMsg://在线文档消息
                                                            msgModel = JsonHelper.JsonToT<ChatDocMsg>(chatJsonStr);
                                                            break;
                                                        case MsgType.Markdown://MarkDown格式消息
                                                            msgModel = JsonHelper.JsonToT<ChatMarkDown>(chatJsonStr);
                                                            break;
                                                        case MsgType.News://图文消息
                                                            msgModel = JsonHelper.JsonToT<ChatNews>(chatJsonStr);
                                                            break;
                                                        case MsgType.Calendar://日程消息
                                                            msgModel = JsonHelper.JsonToT<ChatCalendar>(chatJsonStr);
                                                            break;
                                                        case MsgType.Mixed://混合消息
                                                            msgModel = JsonHelper.JsonToT<ChatMixed>(chatJsonStr);
                                                            break;
                                                        case MsgType.MeetingVoiceCall://音频存档消息
                                                            {
                                                                var chatMeetingVoiceCall = JsonHelper.JsonToT<ChatMeetingVoiceCall>(chatJsonStr);
                                                                msgModel = chatMeetingVoiceCall;
                                                                fileName = $"{Guid.NewGuid()}.mp4";
                                                                md5Sum = "";
                                                                sdkFileId = chatMeetingVoiceCall.meeting_voice_call.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "video",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatMeetingVoiceCall.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.VoipDocShare://音频共享文档消息
                                                            {
                                                                var chatVoipDocShare = JsonHelper.JsonToT<ChatVoipDocShare>(chatJsonStr);
                                                                msgModel = chatVoipDocShare;
                                                                fileName = $"{chatVoipDocShare.voip_doc_share.filename}";
                                                                md5Sum = chatVoipDocShare.voip_doc_share.md5sum; ;
                                                                sdkFileId = chatVoipDocShare.voip_doc_share.sdkfileid;

                                                                if (!fileList.Any(t => t.SdkFileId == chatVoipDocShare.voip_doc_share.sdkfileid))
                                                                {
                                                                    fileData = new FileData()
                                                                    {
                                                                        MsgType = "file",
                                                                        MsgDate = TimeHelper.GetUnixTimeToDateTime(chatVoipDocShare.msgtime.ToString()),
                                                                        FileName = fileName,
                                                                        Md5Sum = md5Sum,
                                                                        SdkFileId = sdkFileId
                                                                    };
                                                                    fileList.Add(fileData);
                                                                }
                                                            }
                                                            break;
                                                        case MsgType.ExternalRedPacket://互通红包消息
                                                            msgModel = JsonHelper.JsonToT<ChatExternalRedPacket>(chatJsonStr);
                                                            break;
                                                        case MsgType.SphFeed://视频号消息
                                                            msgModel = JsonHelper.JsonToT<ChatSphFeed>(chatJsonStr);
                                                            break;
                                                        case MsgType.VoipText://音视频通话
                                                            msgModel = JsonHelper.JsonToT<ChatVoipText>(chatJsonStr);
                                                            break;
                                                        case MsgType.QyDiskFile://微盘文件 
                                                            msgModel = JsonHelper.JsonToT<ChatQyDiskFile>(chatJsonStr);
                                                            break;
                                                    }
                                                    break;
                                                default:
                                                    break;
                                            }

                                            /*
                                            图片
                                            {
                                                "msgid":"CAQQvPnc4QUY0On2rYSAgAMgooLa0Q8=","action":"send","from":"XuJinSheng","tolist":["icefog"],"roomid":"",
                                                "msgtime":0,"msgtype":"image",
                                                "image":{"md5sum":"50de8e5ae8ffe4f1df7a93841f71993a","filesize":70961,"sdkfileid":"CtYBMzA2OTAyMDEwMjA0NjIzMDYwMDIwMTAwMDIwNGI3ZmU0MDZlMDIwMzBmNTliMTAyMDQ1YzliNTQ3NzAyMDQ1YzM3M2NiYzA0MjQ2NjM0MzgzNTM0NjEzNTY1MmQzNDYxMzQzODJkMzQzMTYxNjEyZDM5NjEzOTM2MmQ2MTM2NjQ2NDY0NjUzMDY2NjE2NjM1MzcwMjAxMDAwMjAzMDExNTQwMDQxMDUwZGU4ZTVhZThmZmU0ZjFkZjdhOTM4NDFmNzE5OTNhMDIwMTAyMDIwMTAwMDQwMBI4TkRkZk1UWTRPRGcxTVRBek1ETXlORFF6TWw4eE9UUTVOamN6TkRZMlh6RTFORGN4TWpNNU1ERT0aIGEwNGQwYWUyM2JlYzQ3NzQ5MjZhNWZjMjk0ZTEyNTkz"}
                                            }
                                            语音
                                            {
                                                "msgid":"10958372969718811103_1603875609","action":"send","from":"wmGAgeDQAAdBjb8CK4ieMPRm7Cqm-9VA","tolist":["kenshin"],"roomid":"",
                                                "msgtime":1603875609704,"msgtype":"voice",
                                                "voice":{"md5sum":"9db09c7fa627c9e53f17736c786a74d5","voice_size":6810,"play_length":10,"sdkfileid":"kcyZjZqOXhETGYxajB2Zkp5Rk8zYzh4RVF3ZzZGdXlXNWRjMUoxVGZxbzFTTDJnQ2YxL0NraVcxUUJNK3VUamhEVGxtNklCbjZmMEEwSGRwN0h2cU1GQTU1MDRSMWdTSmN3b25ZMkFOeG5hMS90Y3hTQ0VXRlVxYkR0Ymt5c3JmV2VVcGt6UlNXR1ZuTFRWVGtudXVldDRjQ3hscDBrMmNhMFFXVnAwT3Y5NGVqVGpOcWNQV2wrbUJwV01TRm9xWmNDRVVrcFY5Nk9OUS9GbXIvSmZvOVVZZjYxUXBkWnMvUENkVFQxTHc2N0drb2pJT0FLZnhVekRKZ1FSNDU3ZnZtdmYvTzZDOG9DRXl2SUNIOHc9PRI0TkRkZk56ZzRNVE13TVRjMk5qQTRNak0yTmw4ek5qRTVOalExTjE4eE5qQXpPRGMxTmpBNRogNzM3MDY2NmM2YTc5Njg3NDdhNzU3NDY0NzY3NTY4NjY="}
                                            }
                                            视频
                                            {
                                                "msgid":"17955920891003447432_1603875627","action":"send","from":"kenshin","tolist":["wmGAgeDQAAHuRJbt4ZQI_1cqoQcf41WQ"],"roomid":"",
                                                "msgtime":1603875626823,"msgtype":"video",
                                                "video":{"md5sum":"d06fc80c01d6fbffcca3b229ba41eac6","filesize":15169724,"play_length":108,"sdkfileid":"MzAzMjYxMzAzNTYzMzgzMjMyMzQwMjAxMDAwMjA0MDBlNzc4YzAwNDEwZDA2ZmM4MGMwMWQ2ZmJmZmNjYTNiMjI5YmE0MWVhYzYwMjAxMDQwMjAxMDAwNDAwEjhORGRmTVRZNE9EZzFNREEyTlRjM056QXpORjgxTWpZeE9USTBOek5mTVRZd016ZzNOVFl5Tnc9PRogNTIzNGQ1NTQ5N2RhNDM1ZDhlZTU5ODk4NDQ4NzRhNDk="}
                                            }
                                            表情
                                            {
                                                "msgid":"6623217619416669654_1603875612","action":"send","from":"icef","tolist":["wmErxtDgAAhteCglUZH2kUt3rq431qmg"],"roomid":"",
                                                "msgtime":1603875611148,"msgtype":"emotion",
                                                "emotion":{"type":1,"width":290,"height":290,"imagesize":962604,"md5sum":"94c2b0bba52cc456cb8221b248096612","sdkfileid":"4eE1ESTVNalE1TnprMFh6RTJNRE00TnpVMk1UST0aIDc0NzI2NjY1NzE3NTc0Nzg2ZDZlNzg2YTY5NjY2MTYx"}
                                            }
                                            文件
                                            {
                                                "msgid":"18039699423706571225_1603875608","action":"send","from":"kens","tolist":["wmErxtDgAArDlFIhf76O6w4GxU81al8w"],"roomid":"",
                                                "msgtime":1603875608214,"msgtype":"file",
                                                "file":{"md5sum":"18e93fc2ea884df23b3d2d3b8667b9f0","filename":"资料.docx","fileext":"docx","filesize":18181,"sdkfileid":"E4ODRkZjIzYjNkMmQzYjg2NjdiOWYwMDIwMTA1MDIwMTAwMDQwMBI4TkRkZk1UWTRPRGcxTURrek9UZzBPVEF6TTE4eE1EUXpOVGcxTlRVNVh6RTJNRE00TnpVMk1EZz0aIDMwMzkzMzY0NjEzNjM3NjY2NDY1NjMzNjYxMzIzNzYx"}
                                            }
                                            会话记录消息
                                            ype	每条聊天记录的具体消息类型:ChatRecordText/ ChatRecordFile/ ChatRecordImage/ ChatRecordVideo/ ChatRecordLink/ ChatRecordLocation/ ChatRecordMixed ….
                                            {
                                                "msgid":"11354299838102555191_1603875658","action":"send","from":"ken","tolist":["icef"],"roomid":"",
                                                "msgtime":1603875657905,"msgtype":"chatrecord",
                                                "chatrecord":{
                                                    "title":"群聊",
                                                    "item":[
                                                        {"type":"ChatRecordText","msgtime":1603875610,"content":"{\"content\":\"test\"}","from_chatroom":false},
                                                        {"type":"ChatRecordText","msgtime":1603875620,"content":"{\"content\":\"test2\"}","from_chatroom":false}
                                                    ]
                                                }
                                            }

                                            混合消息
                                            mixed	消息内容。可包含图片、文字、表情等多种消息。Object类型
                                            {
                                                "msgid":"DAQQluDa4QUY0On4kYSABAMgzPrShAE=","action":"send","from":"HeMiao","tolist":["HeChangTian","LiuZeYu"],"roomid":"wr_tZ2BwAAUwHpYMwy9cIWqnlU3Hzqfg",
                                                "msgtime":1577414359072,"msgtype":"mixed",
                                                "mixed":{
                                                    "item":[
                                                        {"type":"text","content":"{\"content\":\"你好[微笑]\\n\"}"},
                                                        {"type":"image","content":"{\"md5sum\":\"368b6c18c82e6441bfd89b343e9d2429\",\"filesize\":13177,\"sdkfileid\":\"CtYBMzA2OTAyMDEwMjA0NjIzMDYwMDIwMTAwMDWwNDVmYWY4Y2Q3MDIwMzBmNTliMTAyMDQwYzljNTQ3NzAyMDQ1ZTA1NmFlMjA0MjQ2NjM0NjIzNjY2MzYzNTMyMmQzNzYxMzQ2NDJkMzQ2MjYxNjQyZDM4MzMzMzM4MmQ3MTYyMzczMTM4NjM2NDYxMzczMjY2MzkwMjAxMDAwMjAzMDIwMDEwMDQxMDM2OGI2YzE4YzgyZTY0NDFiZmQ4OWIyNDNlOWQyNDI4MDIwMTAyMDIwMTAwMDQwMBI4TkRkZk2UWTRPRGcxTVRneE5URTFNRGc1TVY4eE1UTTFOak0yTURVeFh6RTFOemMwTVRNek5EYz0aIDQzMTY5NDFlM2MxZDRmZjhhMjEwY2M0NDQzZGUXOTEy\"}"}
                                                    ]
                                                }
                                            }
                                            音频存档消息
                                            {
                                                "msgid":"17952229780246929345_1594197637","action":"send","from":"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA","tolist":["wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA"],
                                                "msgtime":1594197581203,"msgtype":"meeting_voice_call","voiceid":"grb8a4c48a3c094a70982c518d55e40557",
                                                "meeting_voice_call":{
                                                    "endtime":1594197635,"sdkfileid":"CpsBKjAqd0xhb2JWRUJldGtwcE5DVTB6UjRUalN6c09vTjVyRnF4YVJ5M24rZC9YcHF3cHRPVzRwUUlaMy9iTytFcnc0SlBkZDU1YjRNb0MzbTZtRnViOXV5WjUwZUIwKzhjbU9uRUlxZ3pyK2VXSVhUWVN2ejAyWFJaTldGSkRJVFl0aUhkcVdjbDJ1L2RPbjJsRlBOamJaVDNnPT0SOE5EZGZNVFk0T0RnMU16YzVNVGt5T1RJMk9GOHhNalk0TXpBeE9EZzJYekUxT1RReE9UYzJNemM9GiA3YTYyNzA3NTY4Nzc2MTY3NzQ2MTY0NzA2ZTc4NjQ2OQ==",
                                                    "demofiledata":[{"filename":"65eb1cdd3e7a3c1740ecd74220b6c627.docx","demooperator":"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA","starttime":1594197599,"endtime":1594197609}],
                                                    "sharescreendata":[{"share":"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA","starttime":1594197624,"endtime":1594197624}]
                                                }
                                            } 
                                            音频共享文档消息
                                            {
                                                "msgid":"16527954622422422847_1594199256","action":"send","from":"18002520162","tolist":["wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA"],
                                                "msgtime":1594199235014,"msgtype":"voip_doc_share","voipid":"gr2751c98b19300571f8afb3b74514bd32",
                                                "voip_doc_share":{
                                                    "filename":"欢迎使用微盘.pdf.pdf","md5sum":"ff893900f24e55e216e617a40e5c4648","filesize":4400654,"sdkfileid":"CpsBKjAqZUlLdWJMd2gvQ1JxMzd0ZjlpdW5mZzJOOE9JZm5kbndvRmRqdnBETjY0QlcvdGtHSFFTYm95dHM2VlllQXhkUUN5KzRmSy9KT3pudnA2aHhYZFlPemc2aVZ6YktzaVh3YkFPZHlqNnl2L2MvcGlqcVRjRTlhZEZsOGlGdHJpQ2RWSVNVUngrVFpuUmo3TGlPQ1BJemlRPT0SOE5EZGZNVFk0T0RnMU16YzVNVGt5T1RJMk9GODFNelUyTlRBd01qQmZNVFU1TkRFNU9USTFOZz09GiA3YTcwNmQ2Zjc5NjY3MDZjNjY2Zjc4NzI3NTZmN2E2YQ=="
                                                }
                                            }
                                            */
                                            #endregion
                                            if (msgModel != null)
                                            {
                                                msgModel.seq = msgseq;
                                                msgList.Add(msgModel);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        Log.Error("FinanceHelper", "企业微信-会话内容存档:会话数据解密失败。");
                                    }
                                }
                                else
                                {
                                    Log.Error("FinanceHelper", "企业微信-会话内容存档:会话数据解密私钥为空。");
                                }
                            }
                        }
                    }
                    else
                    {
                        Log.Error("FinanceHelper", $"企业微信-会话内容存档:转换会话数据阶段返回值错误\n[{encryptChatData.errcode}]{encryptChatData.errmsg}");
                    }
                }
                else
                {
                    Log.Error("FinanceHelper", "企业微信-会话内容存档:获取会话数据阶段返回失败。");
                }
            }
            catch (Exception ex)
            {
                Log.Error("FinanceHelper", "企业微信-会话内容存档:获取会话数据数据异常。" + ex.ToString());
            }
            finally
            {
                Finance.FreeSlice(slice);
            }
            return Tuple.Create(msgList, fileList);
        }

        /// <summary>
        /// 读取媒体数据
        /// 也只能拉取5天内的媒体消息内容
        /// </summary>
        /// <param name="sdkfileid">消息体内容中的sdkfileid信息。</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间,单位秒</param>
        /// <returns>byte[]</returns>
        public byte[] GetMediaData(string sdkfileid, string proxy = "", string passwd = "", long timeout = 300)
        {
            /*
            错误码:
            返回值	说明	建议
            10000	请求参数错误	检查Init接口corpid、secret参数;检查GetChatData接口limit参数是否未填或大于1000;检查GetMediaData接口sdkfileid是否为空,indexbuf是否正常
            10001	网络请求错误	检查是否网络有异常、波动;检查使用代理的情况下代理参数是否设置正确的用户名与密码
            10002	数据解析失败	建议重试请求。若仍失败,可以反馈给企业微信进行查询,请提供sdk接口参数与调用时间点等信息
            10003	系统调用失败	GetMediaData调用失败,建议重试请求。若仍失败,可以反馈给企业微信进行查询,请提供sdk接口参数与调用时间点等信息
            10004	已废弃	        目前不会返回此错误码
            10005	fileid错误	    检查在GetMediaData接口传入的sdkfileid是否正确
            10006	解密失败	    请检查是否先进行base64decode再进行rsa私钥解密,再进行DecryptMsg调用
            10007	已废弃	        目前不会返回此错误码
            10008	DecryptMsg错误	建议重试请求。若仍失败,可以反馈给企业微信进行查询,请提供sdk接口参数与调用时间点等信息
            10009	ip非法	        请检查sdk访问外网的ip是否与管理端设置的可信ip匹配,若不匹配会返回此错误码
            10010	请求的数据过期	用户欲拉取的数据已过期,仅支持近5天内的数据拉取
            10011	ssl证书错误	    使用openssl版本sdk,校验ssl证书失败 
            */
            var byteList = new List<byte>();
            try
            {
                var outIndexBuf = "";//媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,后续每次调用只需要将上次调用返回的outindexbuf填入即可。

                while (true)
                {
                    //    int retryCount = 0;

                    //RetryGetMedia:
                    var mediaData = Finance.NewMediaData();
                    var res = Finance.GetMediaData(this.SDK, outIndexBuf, sdkfileid, proxy, passwd, timeout, mediaData);
                    if (res == 0)
                    {
                        var dataIntPtr = Finance.GetData(mediaData);
                        var dataLen = Finance.GetDataLen(mediaData);
                        var bytes = new byte[dataLen];
                        Marshal.Copy(dataIntPtr, bytes, 0, bytes.Length);
                        byteList.AddRange(bytes);

                        // 校验文件是否已经读取完毕
                        if (Finance.IsMediaDataFinish(mediaData) == 1)
                        {
                            Finance.FreeMediaData(mediaData);
                            break;
                        }
                        else
                        {
                            var oibPtr = Finance.GetOutIndexBuf(mediaData);
                            outIndexBuf = Marshal.PtrToStringAnsi(oibPtr);
                            Finance.FreeMediaData(mediaData);
                        }
                    }
                    else
                    {
                        byteList = null;

                        //10001 网络请求错误
                        //10002 解析数据失败
                        //10003 系统调用失败
                        //if (res == 10002 && retryCount < 3)
                        //{
                        //    retryCount++;
                        //    System.Threading.Thread.Sleep(500);
                        //    goto RetryGetMedia;
                        //}
                        //else
                        //{
                        Finance.FreeMediaData(mediaData);
                        //throw new Exception($"企微会话存档:获取会话媒体数据失败,res:{res}");
                        Log.Error("FinanceHelper", $"企业微信-会话内容存档:获取会话媒体数据失败,sdkfileid:{sdkfileid};res:{res}");
                        break;
                        //}
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("FinanceHelper", "企业微信-会话内容存档:获取会话媒体数据失败!" + ex.ToString());
            }
            if (byteList != null && byteList.Count > 0)
            {
                return byteList.ToArray();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取媒体数据列表(含文件byte[])
        /// </summary>
        /// <param name="list">媒体数据列表</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间,单位秒</param>
        /// <returns></returns>
        public List<FileData> GetFileList(List<FileData> list, string proxy = "", string passwd = "", long timeout = 300)
        {
            List<FileData> listNew = null;
            if (list.Count > 0)
            {
                listNew = new List<FileData>();
                foreach (var file in list)
                {
                    byte[] buffer = null;
                    //也只能拉取5天内的媒体消息内容
                    if (file.MsgDate > DateTime.Now.AddDays(-5))
                    {
                        buffer = GetMediaData(file.SdkFileId, proxy, passwd, timeout);
                    }
                    file.FileByte = buffer;
                    listNew.Add(file);
                }
            }
            return listNew;
        }

        #endregion 同步方法

        #region 异步方法

        /// <summary>
        /// 获取会话记录数据(异步方法)
        /// 注:获取会话记录内容不能超过5天,如果企业需要全量数据,则企业需要定期拉取聊天消息。返回的ChatDatas内容为json格式。
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型,范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值,不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长,单位 秒</param>
        /// <returns>byte[]</returns>
        public async Task<Tuple<List<IMsgBase>, List<FileData>>> GetChatDataAsync(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 300)
        {
            // 调用同步方法并转换为异步
            var list = GetChatData(seq, limit, proxy, passwd, timeout);
            return await Task.FromResult(list);
        }

        /// <summary>
        /// 循环获取会话记录数据
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型,范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值,不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长,单位 秒</param>
        /// <returns></returns>
        public async Task<Tuple<List<IMsgBase>, List<FileData>>> GetChatListAsync(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 60)
        {
            List<IMsgBase> msgList = new List<IMsgBase>();
            List<FileData> fileList = new List<FileData>();
            DateTime startTime = DateTime.Now;
            int pullNumber = 0; //拉取的次数
            //获取聊天信息
            bool isGet = true;
            while (isGet)
            {
                var result = await GetChatDataAsync(seq, limit, proxy, passwd, timeout);
                List<IMsgBase> list1 = result.Item1; //企业微信 会话内容 列表
                List<FileData> list2 = result.Item2; //媒体内容 列表

                var count = list1.Count;
                if (count > 0)
                {
                    foreach (var item in list1)
                    {
                        msgList.Add(item);
                    }
                    foreach (var item in list2)
                    {
                        fileList.Add(item);
                    }

                    if (count < MaxLimit)
                    {
                        //总数没有超过 限制拉取的数量时 说明已经拉取完成 不在循环拉取了
                        if (count >= limit)
                        {
                            seq = list1.OrderByDescending(t => t.seq).Select(q => q.seq).FirstOrDefault();
                        }
                        else
                        {
                        isGet = false;
                        }
                    }
                    else
                    {
                        isGet = false;
                    }
                }
                else
                {
                    isGet = false;
                }

                //看看 调用频率不可超过600次 / 分钟。
                pullNumber++;

                DateTime endTime = DateTime.Now;
                TimeSpan timeDifference = endTime - startTime;
                double secondsDifference = timeDifference.TotalSeconds;
                if (secondsDifference < 60)
                {
                    if (pullNumber >= 600)
                    {
                        isGet = false;
                    }
                }
            }
            return Tuple.Create(msgList, fileList);
        }

        /// <summary>
        /// 读取媒体数据(异步方法)
        /// 也只能拉取5天内的媒体消息内容
        /// </summary>
        /// <param name="sdkfileid">消息体内容中的sdkfileid信息。</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间,单位秒</param>
        /// <returns>byte[]</returns>
        public async Task<byte[]> GetMediaDataAsync(string sdkfileid, string proxy = "", string passwd = "", long timeout = 300)
        {
            // 调用同步方法并转换为异步
            var bytes = GetMediaData(sdkfileid, proxy, passwd, timeout);
            return await Task.FromResult(bytes);
        }

        /// <summary>
        /// 获取媒体数据列表(含文件byte[])
        /// </summary>
        /// <param name="list">媒体数据列表</param>
        /// <param name="proxy">使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间,单位秒</param>
        /// <returns></returns>
        public async Task<List<FileData>> GetFileListAync(List<FileData> list, string proxy = "", string passwd = "", long timeout = 300)
        {
            List<FileData> listNew = null;
            if (list.Count > 0)
            {
                listNew = new List<FileData>();
                foreach (var file in list)
                {
                    byte[] buffer = null;
                    //也只能拉取5天内的媒体消息内容
                    if (file.MsgDate > DateTime.Now.AddDays(-5))
                    {
                        buffer = await GetMediaDataAsync(file.SdkFileId, proxy, passwd, timeout);
                    }
                    file.FileByte = buffer;
                    listNew.Add(file);
                }
            }
            return listNew;
        }

        #endregion 异步方法

        /// <summary>
        /// 获取文本
        /// </summary>
        /// <param name="slice"></param>
        /// <returns></returns>
        public string GetContentFromSlice(IntPtr slice)
        {
            var length = Finance.GetSliceLen(slice);
            var bytes = new byte[length];
            var ptr = Finance.GetContentFromSlice(slice);
            Marshal.Copy(ptr, bytes, 0, bytes.Length);
            return Encoding.UTF8.GetString(bytes);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="privateKey">私钥 “-----BEGIN RSA PRIVATE KEY-----”开头 “-----END RSA PRIVATE KEY-----”结尾</param>
        /// <param name="text">使用publickey_ver指定版本的公钥进行非对称加密后base64加密的内容</param>
        /// <returns></returns>
        public string Decrypt(string privateKey, string text)
        {
            var rsa = new RSACryptoServiceProvider();
            var bytes = Convert.FromBase64String(text);
            rsa.FromXmlString(RSAKeyConvert.PrivateKeyPemToXml(privateKey));
            var result = rsa.Decrypt(bytes, false);
            return Encoding.UTF8.GetString(result);
        }
    }
}

三、代码调用实例


string corpid = ""; //企业微信ID
string secret = ""; //会话内容存档 secret
string privateKey1 = @"-----BEGIN RSA PRIVATE KEY----- 开头 -----END RSA PRIVATE KEY----- 结尾"; //会话内容存档 加密 私钥及对应版本号 pem格式的私钥
string privateKey2 = @"-----BEGIN RSA PRIVATE KEY----- 开头 -----END RSA PRIVATE KEY----- 结尾"; //会话内容存档 加密 私钥及对应版本号 pem格式的私钥
Dictionary<int, string> verKey = new Dictionary<int, string>();
verKey.Add(1, privateKey1);
verKey.Add(2, privateKey2);


var seq = 0; //这个可以第一次使用为0 下一次使用为上一次拉取时保存的最大值(下面有注释)
var client = new FinanceHelper(corpid, secret, verKey);

//同步方法
var result = client.GetChatList(seq);
//异步方法
//var result = await client.GetChatListAsync(seq);

List<IMsgBase> msgList = result.Item1; //企业微信 会话内容 列表
List<FileData> fileList = result.Item2; //媒体内容 列表

//msgList 中最大的seq 值  要保存好 下次拉取数据时要带入  
//同一个企业要根据 MsgId 消息去重 
//fileList 这个媒体数据 也要根据 SdkFileId 去重

四、相关项目源代码,开源地址:

1.gitee地址:WeWork: 企业微信会话内容存档 (gitee.com)

2.CSDN地址:【免费】C#.NET企业微信会话内容存档资源-CSDN文库

代码开源不易,如果您觉得有用,求打赏:

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wsk198726

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值