2
0

WXUserReader.cs 27 KB

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