HtmlExport.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using K4os.Compression.LZ4.Encoders;
  2. using K4os.Compression.LZ4;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using WechatPCMsgBakTool.Interface;
  10. using WechatPCMsgBakTool.Model;
  11. using System.Xml;
  12. namespace WechatPCMsgBakTool
  13. {
  14. public class HtmlExport : IExport
  15. {
  16. private string HtmlBody = "";
  17. private WXSession? Session = null;
  18. public void InitTemplate(WXSession session)
  19. {
  20. Session = session;
  21. HtmlBody = "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>溯雪微信聊天记录备份工具</title><style>p{margin:0px;}.msg{padding-bottom:10px;}.nickname{font-size:10px;}.content{font-size:14px;}</style></head><body>";
  22. HtmlBody += string.Format("<div class=\"msg\"><p class=\"nickname\"><b>与 {0}({1}) 的聊天记录</b></p>", Session.NickName, Session.UserName);
  23. HtmlBody += string.Format("<div class=\"msg\"><p class=\"nickname\"><b>导出时间:{0}</b></p><hr/>", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  24. }
  25. public void InitTemplate(WXContact contact)
  26. {
  27. WXSession session = new WXSession();
  28. session.NickName = contact.NickName;
  29. session.UserName = contact.UserName;
  30. InitTemplate(session);
  31. }
  32. public void Save(string path = "",bool append = false)
  33. {
  34. if (!append)
  35. {
  36. File.WriteAllText(path, HtmlBody);
  37. }
  38. else
  39. {
  40. File.AppendAllText(path, HtmlBody);
  41. HtmlBody = "";
  42. }
  43. }
  44. public void SetEnd()
  45. {
  46. HtmlBody += "</body></html>";
  47. }
  48. public void SetMsg(WXUserReader reader,WXContact contact)
  49. {
  50. if (Session == null)
  51. throw new Exception("请初始化模版:Not Use InitTemplate");
  52. List<WXMsg>? msgList = reader.GetWXMsgs(contact.UserName);
  53. if (msgList == null)
  54. throw new Exception("获取消息失败,请确认数据库读取正常");
  55. msgList.Sort((x, y) => x.CreateTime.CompareTo(y.CreateTime));
  56. foreach (var msg in msgList)
  57. {
  58. HtmlBody += string.Format("<div class=\"msg\"><p class=\"nickname\">{0} <span style=\"padding-left:10px;\">{1}</span></p>", msg.IsSender ? "我" : Session.NickName, TimeStampToDateTime(msg.CreateTime).ToString("yyyy-MM-dd HH:mm:ss"));
  59. if (msg.Type == 1)
  60. HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", msg.StrContent);
  61. else if (msg.Type == 3)
  62. {
  63. string? path = reader.GetAttachment(WXMsgType.Image, msg);
  64. if (path == null)
  65. {
  66. HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "图片转换出现错误或文件不存在");
  67. continue;
  68. }
  69. HtmlBody += string.Format("<p class=\"content\"><img src=\"{0}\" style=\"max-height:1000px;max-width:1000px;\"/></p></div>", path);
  70. }
  71. else if (msg.Type == 43)
  72. {
  73. string? path = reader.GetAttachment(WXMsgType.Video, msg);
  74. if (path == null)
  75. {
  76. HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "视频不存在");
  77. continue;
  78. }
  79. HtmlBody += string.Format("<p class=\"content\"><video controls style=\"max-height:300px;max-width:300px;\"><source src=\"{0}\" type=\"video/mp4\" /></video></p></div>", path);
  80. }
  81. else if(msg.Type== 49)
  82. {
  83. using (var decoder = LZ4Decoder.Create(true, 64))
  84. {
  85. byte[] target = new byte[10240];
  86. int res = 0;
  87. if(msg.CompressContent != null)
  88. res = LZ4Codec.Decode(msg.CompressContent, 0, msg.CompressContent.Length, target, 0, target.Length);
  89. byte[] data = target.Skip(0).Take(res).ToArray();
  90. string xml = Encoding.UTF8.GetString(data);
  91. if (!string.IsNullOrEmpty(xml))
  92. {
  93. xml = xml.Replace("\n", "");
  94. XmlDocument xmlObj = new XmlDocument();
  95. xmlObj.LoadXml(xml);
  96. if(xmlObj.DocumentElement != null)
  97. {
  98. string title = "";
  99. string appName = "";
  100. string url = "";
  101. XmlNodeList? findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/title");
  102. if(findNode != null)
  103. {
  104. if(findNode.Count > 0)
  105. {
  106. title = findNode[0]!.InnerText;
  107. }
  108. }
  109. findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/sourcedisplayname");
  110. if (findNode != null)
  111. {
  112. if (findNode.Count > 0)
  113. {
  114. appName = findNode[0]!.InnerText;
  115. }
  116. }
  117. findNode = xmlObj.DocumentElement.SelectNodes("/msg/appmsg/url");
  118. if (findNode != null)
  119. {
  120. if (findNode.Count > 0)
  121. {
  122. url = findNode[0]!.InnerText;
  123. }
  124. }
  125. HtmlBody += string.Format("<p class=\"content\">{0}|{1}</p><p><a href=\"{2}\">点击访问</a></p></div>", appName, title, url);
  126. }
  127. }
  128. }
  129. }
  130. else if (msg.Type == 34)
  131. {
  132. string? path = reader.GetAttachment(WXMsgType.Audio, msg);
  133. if (path == null)
  134. {
  135. HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "语音不存在");
  136. continue;
  137. }
  138. HtmlBody += string.Format("<p class=\"content\"><audio controls src=\"{0}\"></audio></p></div>", path);
  139. }
  140. else
  141. {
  142. HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "暂未支持的消息");
  143. }
  144. }
  145. }
  146. private static DateTime TimeStampToDateTime(long timeStamp, bool inMilli = false)
  147. {
  148. DateTimeOffset dateTimeOffset = inMilli ? DateTimeOffset.FromUnixTimeMilliseconds(timeStamp) : DateTimeOffset.FromUnixTimeSeconds(timeStamp);
  149. return dateTimeOffset.LocalDateTime;
  150. }
  151. }
  152. }