WXUserReader.cs 26 KB

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