2
0

WXUserReader.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. using K4os.Compression.LZ4.Encoders;
  2. using K4os.Compression.LZ4;
  3. using SQLite;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Net.Mime;
  11. using System.Security.Cryptography;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using System.Windows.Interop;
  15. using System.Windows.Media.Imaging;
  16. using System.Xml;
  17. using System.Xml.Linq;
  18. using WechatBakTool.Helpers;
  19. using WechatBakTool.Model;
  20. using System.Windows;
  21. using System.Net.Http;
  22. using System.Reflection.Metadata;
  23. using System.Threading;
  24. using Newtonsoft.Json;
  25. using WechatBakTool.ViewModel;
  26. namespace WechatBakTool
  27. {
  28. public class WXUserReader
  29. {
  30. private Dictionary<string, SQLiteConnection> DBInfo = new Dictionary<string, SQLiteConnection>();
  31. private UserBakConfig? UserBakConfig = null;
  32. private Hashtable HeadImgCache = new Hashtable();
  33. private Hashtable UserNameCache = new Hashtable();
  34. private Hashtable EmojiCache = new Hashtable();
  35. private HttpClient httpClient = new HttpClient();
  36. public WXUserReader(UserBakConfig userBakConfig) {
  37. string path = Path.Combine(userBakConfig.UserWorkspacePath, "DecDB");
  38. UserBakConfig = userBakConfig;
  39. LoadDB(path);
  40. InitCache();
  41. EmojiCacheInit();
  42. }
  43. public void LoadDB(string path)
  44. {
  45. string[] dbFileList = Directory.GetFiles(path);
  46. foreach (var item in dbFileList)
  47. {
  48. FileInfo fileInfo = new FileInfo(item);
  49. if (fileInfo.Extension != ".db")
  50. continue;
  51. SQLiteConnection con = new SQLiteConnection(item);
  52. string dbName = fileInfo.Name.Split('.')[0];
  53. if (DBInfo.ContainsKey(dbName))
  54. {
  55. continue;
  56. }
  57. DBInfo.Add(dbName, con);
  58. }
  59. }
  60. private SQLiteConnection? getCon(string name)
  61. {
  62. if (DBInfo.ContainsKey(name))
  63. {
  64. return DBInfo[name];
  65. }
  66. else
  67. {
  68. return null;
  69. }
  70. }
  71. public void InitCache()
  72. {
  73. SQLiteConnection? con = getCon("Misc");
  74. if (con == null)
  75. return;
  76. string query = @"SELECT * FROM ContactHeadImg1";
  77. List<ContactHeadImg> imgs = con.Query<ContactHeadImg>(query);
  78. foreach(ContactHeadImg item in imgs)
  79. {
  80. if (!HeadImgCache.ContainsKey(item.usrName))
  81. {
  82. HeadImgCache.Add(item.usrName, item);
  83. }
  84. }
  85. List<WXContact> contacts = GetWXContacts(null, true).ToList();
  86. foreach(WXContact contact in contacts)
  87. {
  88. if (!UserNameCache.ContainsKey(contact.UserName))
  89. UserNameCache.Add(contact.UserName, contact);
  90. }
  91. }
  92. public void EmojiCacheInit()
  93. {
  94. string emoji_path = Path.Combine(UserBakConfig!.UserWorkspacePath, "Emoji");
  95. if (Directory.Exists(emoji_path))
  96. {
  97. string[] files = Directory.GetFiles(emoji_path);
  98. foreach (string file in files)
  99. {
  100. FileInfo fileInfo = new FileInfo(file);
  101. string[] names = fileInfo.Name.Split(".");
  102. if (!EmojiCache.ContainsKey(names[0]))
  103. {
  104. EmojiCache.Add(names[0], 1);
  105. }
  106. }
  107. }
  108. }
  109. public void PreDownloadEmoji(string username = "")
  110. {
  111. if (UserBakConfig == null)
  112. return;
  113. HttpClientHandler handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate };
  114. HttpClient httpClient = new HttpClient(handler);
  115. List<WXMsg> msgs = GetTypeMsg("47", username);
  116. int i = 0;
  117. // 下载前的Emoji Cache不用做了,在Init的时候已经做了
  118. foreach (var msg in msgs)
  119. {
  120. i++;
  121. if (i % 5 == 0)
  122. {
  123. // 每5次让下载线程休息1秒
  124. Thread.Sleep(1000);
  125. }
  126. try
  127. {
  128. XmlDocument xmlDocument = new XmlDocument();
  129. xmlDocument.LoadXml(msg.StrContent);
  130. XmlNode? node = xmlDocument.SelectSingleNode("/msg/emoji");
  131. if (node != null)
  132. {
  133. if (node.Attributes != null)
  134. {
  135. string type = "";
  136. string md5 = "";
  137. string url = "";
  138. XmlNode? item = node.Attributes.GetNamedItem("type");
  139. type = item != null ? item.InnerText : "";
  140. item = node.Attributes.GetNamedItem("md5");
  141. md5 = item != null ? item.InnerText : "";
  142. item = node.Attributes.GetNamedItem("cdnurl");
  143. url = item != null ? item.InnerText : "";
  144. if (EmojiCache.ContainsKey(md5))
  145. {
  146. i--;
  147. continue;
  148. }
  149. if (url == "")
  150. {
  151. i--;
  152. continue;
  153. }
  154. else
  155. {
  156. string path = Path.Combine(UserBakConfig.UserWorkspacePath, msg.StrTalker, "Emoji", md5 + ".gif");
  157. try
  158. {
  159. HttpResponseMessage res = httpClient.GetAsync(url).Result;
  160. if (res.IsSuccessStatusCode)
  161. {
  162. using (FileStream fs = File.Create(path))
  163. {
  164. res.Content.ReadAsStream().CopyTo(fs);
  165. }
  166. }
  167. }
  168. catch (Exception ex)
  169. {
  170. }
  171. }
  172. }
  173. }
  174. }
  175. catch (Exception ex)
  176. {
  177. }
  178. }
  179. // 下载完成后可能变化,检查一下
  180. EmojiCacheInit();
  181. }
  182. public byte[]? GetHeadImgCahce(string username)
  183. {
  184. if (HeadImgCache.ContainsKey(username))
  185. {
  186. ContactHeadImg? img = HeadImgCache[username] as ContactHeadImg;
  187. if (img == null)
  188. return null;
  189. else
  190. return img.smallHeadBuf;
  191. }
  192. return null;
  193. }
  194. public int[] GetWXCount()
  195. {
  196. SQLiteConnection? con = getCon("MicroMsg");
  197. if (con == null)
  198. return new int[] { 0, 0 };
  199. string query = @"select count(*) as count from contact where type != 4";
  200. int userCount = con.Query<WXCount>(query)[0].Count;
  201. int msgCount = 0;
  202. for (int i = 0; i <= 99; i++)
  203. {
  204. con = getCon("MSG" + i.ToString());
  205. if (con == null)
  206. return new int[] { userCount, msgCount };
  207. query = "select count(*) as count from MSG";
  208. msgCount += con.Query<WXCount>(query)[0].Count;
  209. }
  210. return new int[] { userCount, msgCount };
  211. }
  212. public ObservableCollection<WXContact> GetWXContacts(string? name = null,bool all = false)
  213. {
  214. SQLiteConnection? con = getCon("MicroMsg");
  215. if (con == null)
  216. return new ObservableCollection<WXContact>();
  217. string query = @"select contact.*,session.strContent,contactHeadImgUrl.smallHeadImgUrl,contactHeadImgUrl.bigHeadImgUrl from contact
  218. left join session on session.strUsrName = contact.username
  219. left join contactHeadImgUrl on contactHeadImgUrl.usrName = contact.username
  220. where type != 4 {searchName}
  221. order by nOrder desc";
  222. if (all)
  223. {
  224. query = query.Replace("where type != 4 ", "");
  225. }
  226. List<WXContact>? contacts = null;
  227. if (name != null)
  228. {
  229. query = query.Replace("{searchName}", " and (username like ? or alias like ? or nickname like ? or remark like ?)");
  230. contacts = con.Query<WXContact>(query, $"%{name}%", $"%{name}%", $"%{name}%", $"%{name}%");
  231. }
  232. else
  233. {
  234. query = query.Replace("{searchName}", "");
  235. contacts = con.Query<WXContact>(query);
  236. }
  237. foreach (WXContact contact in contacts)
  238. {
  239. if(contact.Remark != "")
  240. contact.NickName = contact.Remark;
  241. byte[]? imgBytes = GetHeadImgCahce(contact.UserName);
  242. if (imgBytes != null)
  243. {
  244. try
  245. {
  246. MemoryStream stream = new MemoryStream(imgBytes);
  247. BitmapImage bitmapImage = new BitmapImage();
  248. bitmapImage.BeginInit();
  249. bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
  250. bitmapImage.StreamSource = stream;
  251. bitmapImage.EndInit();
  252. bitmapImage.Freeze();
  253. contact.Avatar = bitmapImage;
  254. }
  255. catch
  256. {
  257. #if DEBUG
  258. File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "BitmapConvert Err=>Length", imgBytes.Length));
  259. #endif
  260. }
  261. }
  262. else
  263. continue;
  264. }
  265. return new ObservableCollection<WXContact>(contacts);
  266. }
  267. public List<WXUserImg>? GetUserImgs()
  268. {
  269. SQLiteConnection? con = getCon("MicroMsg");
  270. if (con == null)
  271. return null;
  272. string query = "select * from contactHeadImgUrl";
  273. return con.Query<WXUserImg>(query);
  274. }
  275. public List<WXChatRoom>? GetWXChatRooms()
  276. {
  277. SQLiteConnection? con = getCon("MicroMsg");
  278. if (con == null)
  279. return null;
  280. string query = "select * from ChatRoom";
  281. return con.Query<WXChatRoom>(query);
  282. }
  283. public List<WXMsg> GetTypeMsg(string type,string username)
  284. {
  285. List<WXMsg> tmp = new List<WXMsg>();
  286. for (int i = 0; i <= 99; i++)
  287. {
  288. SQLiteConnection? con = getCon("MSG" + i.ToString());
  289. if (con == null)
  290. return tmp;
  291. List<WXMsg> wXMsgs;
  292. if (username == "")
  293. {
  294. string query = "select * from MSG where Type=?";
  295. wXMsgs = con.Query<WXMsg>(query, type);
  296. }
  297. else
  298. {
  299. string query = "select * from MSG where Type=? and StrTalker = ?";
  300. wXMsgs = con.Query<WXMsg>(query, type, username);
  301. }
  302. tmp.AddRange(wXMsgs);
  303. }
  304. return tmp;
  305. }
  306. public List<WXMsg>? GetWXMsgs(string uid,int time,int page)
  307. {
  308. List<WXMsg> tmp = new List<WXMsg>();
  309. for (int i = 0; i <= 99; i++)
  310. {
  311. SQLiteConnection? con = getCon("MSG" + i.ToString());
  312. if (con == null)
  313. return tmp;
  314. List<WXMsg>? wXMsgs = null;
  315. string query = "select * from MSG where StrTalker=? and CreateTime>? Limit ?";
  316. wXMsgs = con.Query<WXMsg>(query, uid, time, page);
  317. if (wXMsgs.Count != 0) {
  318. return ProcessMsg(wXMsgs, uid);
  319. }
  320. }
  321. return tmp;
  322. }
  323. public List<WXMsg>? GetWXMsgs(string uid,string msg = "")
  324. {
  325. List<WXMsg> tmp = new List<WXMsg>();
  326. for (int i = 0; i <= 99; i++)
  327. {
  328. SQLiteConnection? con = getCon("MSG" + i.ToString());
  329. if (con == null)
  330. return tmp;
  331. List<WXMsg>? wXMsgs = null;
  332. if (msg == "")
  333. {
  334. string query = "select * from MSG where StrTalker=?";
  335. wXMsgs = con.Query<WXMsg>(query, uid);
  336. }
  337. else if (uid == "")
  338. {
  339. string query = "select * from MSG where StrContent like ?";
  340. wXMsgs = con.Query<WXMsg>(query, string.Format("%{0}%", msg));
  341. }
  342. else
  343. {
  344. string query = "select * from MSG where StrTalker=? and StrContent like ?";
  345. wXMsgs = con.Query<WXMsg>(query, uid, string.Format("%{0}%", msg));
  346. }
  347. tmp.AddRange(ProcessMsg(wXMsgs, uid));
  348. }
  349. return tmp;
  350. }
  351. public List<WXMsg>? GetWXMsgs(string uid, DatetimePickerViewModel dateModel)
  352. {
  353. List<WXMsg> tmp = new List<WXMsg>();
  354. for (int i = 0; i <= 99; i++)
  355. {
  356. SQLiteConnection? con = getCon("MSG" + i.ToString());
  357. if (con == null)
  358. return tmp;
  359. List<WXMsg>? wXMsgs = null;
  360. string query = "";
  361. if (dateModel.DateType == 2 || dateModel.DateType == 3)
  362. {
  363. query = "select * from MSG where StrTalker=? and date(createtime,'unixepoch') = ?";
  364. wXMsgs = con.Query<WXMsg>(query, uid, dateModel.PickDate.ToString("yyyy-MM-dd"));
  365. }
  366. else if(dateModel.DateType == 4 )
  367. {
  368. query = "select * from MSG where StrTalker=? and date(createtime,'unixepoch') >= ? and date(createtime,'unixepoch') <= ?";
  369. wXMsgs = con.Query<WXMsg>(query, uid, dateModel.StartDate.ToString("yyyy-MM-dd"), dateModel.EndDate.ToString("yyyy-MM-dd"));
  370. }
  371. else
  372. {
  373. query = "select * from MSG where StrTalker=?";
  374. wXMsgs = con.Query<WXMsg>(query, uid);
  375. }
  376. tmp.AddRange(ProcessMsg(wXMsgs, uid));
  377. }
  378. return tmp;
  379. }
  380. private List<WXMsg> ProcessMsg(List<WXMsg> msgs,string uid)
  381. {
  382. foreach (WXMsg w in msgs)
  383. {
  384. if (UserNameCache.ContainsKey(w.StrTalker))
  385. {
  386. WXContact? contact = UserNameCache[w.StrTalker] as WXContact;
  387. if (contact != null)
  388. {
  389. if (contact.Remark != "")
  390. w.NickName = contact.Remark;
  391. else
  392. w.NickName = contact.NickName;
  393. w.StrTalker = contact.UserName;
  394. }
  395. }
  396. else
  397. {
  398. w.NickName = uid;
  399. }
  400. // 群聊处理
  401. if (uid.Contains("@chatroom"))
  402. {
  403. string userId = "";
  404. if (w.BytesExtra == null)
  405. continue;
  406. string sl = BitConverter.ToString(w.BytesExtra).Replace("-", "");
  407. ProtoMsg protoMsg;
  408. using (MemoryStream stream = new MemoryStream(w.BytesExtra))
  409. {
  410. protoMsg = ProtoBuf.Serializer.Deserialize<ProtoMsg>(stream);
  411. }
  412. if (protoMsg.TVMsg != null)
  413. {
  414. foreach (TVType _tmp in protoMsg.TVMsg)
  415. {
  416. if (_tmp.Type == 1)
  417. userId = _tmp.TypeValue;
  418. }
  419. }
  420. if (!w.IsSender)
  421. {
  422. if (UserNameCache.ContainsKey(userId))
  423. {
  424. WXContact? contact = UserNameCache[userId] as WXContact;
  425. if (contact != null)
  426. w.NickName = contact.Remark == "" ? contact.NickName : contact.Remark;
  427. }
  428. else
  429. {
  430. w.NickName = userId;
  431. }
  432. }
  433. }
  434. // 发送人名字处理
  435. if (w.IsSender)
  436. w.NickName = "我";
  437. w.DisplayContent = w.StrContent;
  438. // 额外格式处理
  439. if (w.Type != 1)
  440. {
  441. if (w.Type == 10000)
  442. {
  443. w.Type = 1;
  444. w.NickName = "系统消息";
  445. w.DisplayContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
  446. }
  447. else if (w.Type == 49 && (w.SubType == 6 || w.SubType == 19 || w.SubType == 40))
  448. {
  449. WXSessionAttachInfo? attachInfos = GetWXMsgAtc(w);
  450. if (attachInfos == null)
  451. {
  452. w.DisplayContent = "附件不存在";
  453. }
  454. else
  455. {
  456. w.DisplayContent = Path.Combine(UserBakConfig!.UserResPath, attachInfos.attachPath);
  457. }
  458. }
  459. else
  460. {
  461. w.DisplayContent = "[界面未支持格式]Type=" + w.Type;
  462. }
  463. }
  464. }
  465. return msgs;
  466. }
  467. public List<WXSessionAttachInfo>? GetWXMsgAtc()
  468. {
  469. SQLiteConnection? con = getCon("MultiSearchChatMsg");
  470. if (con == null)
  471. return null;
  472. string query = "select * from SessionAttachInfo";
  473. List<WXSessionAttachInfo> list = con.Query<WXSessionAttachInfo>(query);
  474. if (list.Count != 0)
  475. {
  476. return list;
  477. }
  478. else
  479. return null;
  480. }
  481. public WXSessionAttachInfo? GetWXMsgAtc(WXMsg msg)
  482. {
  483. SQLiteConnection? con = getCon("MultiSearchChatMsg");
  484. if (con == null)
  485. return null;
  486. string query = "select * from SessionAttachInfo where msgId = ? order by attachsize desc";
  487. List<WXSessionAttachInfo> list = con.Query<WXSessionAttachInfo>(query, msg.MsgSvrID);
  488. if (list.Count != 0)
  489. {
  490. //部分附件可能有md5校验,这里移除校验,给的是正确路径
  491. WXSessionAttachInfo acc = list[0];
  492. int index = acc.attachPath.IndexOf(".dat");
  493. int index2 = acc.attachPath.IndexOf(".dat");
  494. if (acc.attachPath.Length - index > 10 && index != -1)
  495. {
  496. acc.attachPath = acc.attachPath.Substring(0, acc.attachPath.Length - 32);
  497. }
  498. if (acc.attachPath.Length - index2 > 10 && index2 != -1)
  499. {
  500. acc.attachPath = acc.attachPath.Substring(0, acc.attachPath.Length - 32);
  501. }
  502. return acc;
  503. }
  504. else
  505. return null;
  506. }
  507. public WXMediaMsg? GetVoiceMsg(WXMsg msg)
  508. {
  509. for (int i = 0; i <= 99; i++)
  510. {
  511. SQLiteConnection? con = getCon("MediaMSG" + i.ToString());
  512. if (con == null)
  513. continue;
  514. string query = "select * from Media where Reserved0=?";
  515. List<WXMediaMsg> wXMsgs = con.Query<WXMediaMsg>(query, msg.MsgSvrID);
  516. if (wXMsgs.Count != 0)
  517. return wXMsgs[0];
  518. }
  519. return null;
  520. }
  521. public string? GetAttachment(WXMsgType type, WXMsg msg)
  522. {
  523. if (UserBakConfig == null)
  524. return null;
  525. string? tmpPath = Path.Combine(UserBakConfig.UserWorkspacePath, msg.StrTalker, "Temp");
  526. if (!Directory.Exists(tmpPath))
  527. Directory.CreateDirectory(tmpPath);
  528. // 这部分是查找
  529. // 如果是图片和视频,从附件库中搜索
  530. string? path = null;
  531. if (type == WXMsgType.Image || type == WXMsgType.Video || type == WXMsgType.File)
  532. {
  533. WXSessionAttachInfo? atcInfo = GetWXMsgAtc(msg);
  534. if (atcInfo == null)
  535. return null;
  536. path = atcInfo.attachPath;
  537. }
  538. // 如果是从语音,从媒体库查找
  539. else if (type == WXMsgType.Audio)
  540. {
  541. WXMediaMsg? voiceMsg = GetVoiceMsg(msg);
  542. if (voiceMsg == null)
  543. return null;
  544. if (voiceMsg.Buf == null)
  545. return null;
  546. // 从DB取音频文件到临时目录
  547. string tmp_file_path = Path.Combine(tmpPath, voiceMsg.Key + ".arm");
  548. using (FileStream stream = new FileStream(tmp_file_path, FileMode.OpenOrCreate))
  549. {
  550. stream.Write(voiceMsg.Buf, 0, voiceMsg.Buf.Length);
  551. }
  552. path = tmp_file_path;
  553. }
  554. else if (type == WXMsgType.Emoji)
  555. {
  556. try
  557. {
  558. XmlDocument xmlDocument = new XmlDocument();
  559. xmlDocument.LoadXml(msg.StrContent);
  560. XmlNode? node = xmlDocument.SelectSingleNode("/msg/emoji");
  561. if (node != null)
  562. {
  563. if (node.Attributes != null)
  564. {
  565. XmlNode? item = node.Attributes.GetNamedItem("md5");
  566. string md5 = item != null ? item.InnerText : "";
  567. if (EmojiCache.ContainsKey(md5))
  568. {
  569. path = string.Format("Emoji\\{0}.gif", md5);
  570. }
  571. }
  572. }
  573. }
  574. catch
  575. {
  576. return null;
  577. }
  578. }
  579. if (path == null)
  580. return null;
  581. // 这部分是解密
  582. // 获取到原路径后,开始进行解密转移,只有图片和语音需要解密,解密后是直接归档目录
  583. if (type == WXMsgType.Image || type == WXMsgType.Audio)
  584. {
  585. path = DecryptAttachment(type, path, msg.StrTalker);
  586. }
  587. else if (type == WXMsgType.Video || type == WXMsgType.File)
  588. {
  589. string to_dir;
  590. if (type == WXMsgType.Video)
  591. to_dir = Path.Combine(UserBakConfig.UserWorkspacePath, msg.StrTalker, "Video");
  592. else
  593. to_dir = Path.Combine(UserBakConfig.UserWorkspacePath, msg.StrTalker, "File");
  594. if (!Directory.Exists(to_dir))
  595. Directory.CreateDirectory(to_dir);
  596. FileInfo fileInfo = new FileInfo(path);
  597. // 目标视频路径
  598. string to_file_path = Path.Combine(to_dir, fileInfo.Name);
  599. // 视频的路径是相对路径,需要加上资源目录
  600. path = Path.Combine(UserBakConfig.UserResPath, path);
  601. // 原文件存在,目标不存在
  602. if (!File.Exists(to_file_path) && File.Exists(path))
  603. {
  604. // 复制
  605. File.Copy(path, to_file_path);
  606. path = to_file_path;
  607. }
  608. else if (File.Exists(to_file_path))
  609. {
  610. path = to_file_path;
  611. }
  612. else
  613. return null;
  614. }
  615. if (path == null)
  616. return null;
  617. // 改相对路径
  618. path = path.Replace(UserBakConfig.UserWorkspacePath + "\\", "");
  619. return path;
  620. }
  621. public string? DecryptAttachment(WXMsgType type, string path,string username)
  622. {
  623. if (UserBakConfig == null)
  624. return null;
  625. string? file_path = null;
  626. switch (type)
  627. {
  628. case WXMsgType.Image:
  629. string img_dir = Path.Combine(UserBakConfig.UserWorkspacePath, username, "Image");
  630. if (!Directory.Exists(img_dir))
  631. Directory.CreateDirectory(img_dir);
  632. // 图片的路径是相对路径,需要加上资源目录
  633. path = Path.Combine(UserBakConfig.UserResPath, path);
  634. if (!File.Exists(path))
  635. return null;
  636. byte[] decFileByte = DecryptionHelper.DecImage(path);
  637. if (decFileByte.Length < 2)
  638. new Exception("解密失败,可能是未支持的格式");
  639. string decFiletype = DecryptionHelper.CheckFileType(decFileByte);
  640. file_path = DecryptionHelper.SaveDecImage(decFileByte, path, img_dir, decFiletype);
  641. break;
  642. case WXMsgType.Audio:
  643. string audio_dir = Path.Combine(UserBakConfig.UserWorkspacePath, username, "Audio");
  644. if (!Directory.Exists(audio_dir))
  645. Directory.CreateDirectory(audio_dir);
  646. FileInfo fileInfo = new FileInfo(path);
  647. string audio_file_dir = Path.Combine(audio_dir, fileInfo.Name + ".mp3");
  648. ToolsHelper.DecodeVoice(path, path + ".pcm", audio_file_dir);
  649. file_path = audio_file_dir;
  650. break;
  651. }
  652. return file_path;
  653. }
  654. public List<WXMsgGroup> GetWXMsgGroup()
  655. {
  656. List<WXMsgGroup> g = new List<WXMsgGroup>();
  657. for (int i = 0; i <= 99; i++)
  658. {
  659. SQLiteConnection? con = getCon("MSG" + i.ToString());
  660. if (con == null)
  661. return g;
  662. string query = "select StrTalker,Count(localId) as MsgCount from MSG GROUP BY StrTalker";
  663. List<WXMsgGroup> wXMsgs = con.Query<WXMsgGroup>(query);
  664. foreach (WXMsgGroup w in wXMsgs)
  665. {
  666. WXMsgGroup? tmp = g.Find(x => x.UserName == w.UserName);
  667. if (tmp == null)
  668. g.Add(w);
  669. else
  670. tmp.MsgCount += g.Count;
  671. }
  672. }
  673. return g;
  674. }
  675. }
  676. public enum WXMsgType
  677. {
  678. Image = 0,
  679. Video = 1,
  680. Audio = 2,
  681. File = 3,
  682. Emoji = 4,
  683. }
  684. }