Browse Source

v0.9.3.0
1.修复一处内存泄露问题。
2.修复部分用户在创建工作区时遇到“未找到适用于完成此操作的图像处理组件。”的问题
3.支持Type=10000的系统消息。
4.新增表情预下载功能,为后期表情支持做准备。

Suxue 1 year ago
parent
commit
2399fa5f4a
7 changed files with 275 additions and 122 deletions
  1. 2 2
      Export/HtmlExport.cs
  2. 3 7
      Model/WXModel.cs
  3. 4 0
      Pages/Manager.xaml
  4. 12 0
      Pages/Manager.xaml.cs
  5. 1 0
      README.md
  6. 250 110
      WXUserReader.cs
  7. 3 3
      WechatBakTool.csproj

+ 2 - 2
Export/HtmlExport.cs

@@ -74,8 +74,8 @@ namespace WechatBakTool.Export
                     if (path == null)
                     {
 #if DEBUG
-                        File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Img Error Path=>", path));
-                        File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),"Img Error Msg=>",JsonConvert.SerializeObject(msg)));
+                        File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Img Error Path=>", path));
+                        File.AppendAllText("debug.log", string.Format("[D]{0} {1}:{2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),"Img Error Msg=>",JsonConvert.SerializeObject(msg)));
 #endif
                         HtmlBody += string.Format("<p class=\"content\">{0}</p></div>", "图片转换出现错误或文件不存在");
                         continue;

+ 3 - 7
Model/WXModel.cs

@@ -10,7 +10,7 @@ using System.Windows.Media.Imaging;
 
 namespace WechatBakTool.Model
 {
-    public class UserBakConfig : INotifyPropertyChanged
+    public class UserBakConfig
     {
         public string UserResPath { get; set; } = "";
         public string UserWorkspacePath { get; set; } = "";
@@ -25,12 +25,6 @@ namespace WechatBakTool.Model
         public string Account { get; set; } = "";
         public string Friends_Number { get; set; } = "-";
         public string Msg_Number { get; set; } = "-";
-
-        public event PropertyChangedEventHandler? PropertyChanged;
-        private void OnPropertyChanged(string propertyName)
-        {
-            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
-        }
     }
 
     public class WXCount
@@ -122,6 +116,8 @@ namespace WechatBakTool.Model
         public int LocalId { get; set; }
         [Column("Type")]
         public int Type { get; set; }
+        [Column("SubType")]
+        public int SubType { get; set; }
         [Column("CreateTime")]
         public long CreateTime { get; set; }
         [Column("IsSender")]

+ 4 - 0
Pages/Manager.xaml

@@ -14,5 +14,9 @@
         <CheckBox Name="cb_user" Margin="90,85" Content="好友聊天" HorizontalAlignment="Left" VerticalAlignment="Top" />
         <Button Name="btn_export_all" Margin="35,110" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_export_all_Click"></Button>
         <Label Content="{Binding LabelStatus}" HorizontalAlignment="Left" Margin="110,110,0,0" VerticalAlignment="Top"/>
+
+        <Label Margin="30,155" Content="表情包预下载" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" />
+        <Button Name="btn_emoji_download" Margin="35,185" Height="26" Width="60" Content="导出" HorizontalAlignment="Left" VerticalAlignment="Top" Background="#2775b6" Foreground="White" BorderThickness="0" Click="btn_emoji_download_Click"></Button>
+
     </Grid>
 </Page>

+ 12 - 0
Pages/Manager.xaml.cs

@@ -1,9 +1,12 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
+using System.Net.Http;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
@@ -13,6 +16,7 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
+using System.Xml;
 using WechatBakTool.Export;
 using WechatBakTool.Model;
 using WechatBakTool.ViewModel;
@@ -86,5 +90,13 @@ namespace WechatBakTool.Pages
             export.SetEnd();
             export.Save(path);
         }
+
+        private void btn_emoji_download_Click(object sender, RoutedEventArgs e)
+        {
+            if (UserReader != null)
+            {
+                UserReader.PreDownloadEmoji();
+            }
+        }
     }
 }

+ 1 - 0
README.md

@@ -12,6 +12,7 @@
 - [x] 语音
 - [x] 分享链接
 - [x] 群聊
+- [x] 系统消息
 - [ ] 文件
 - [ ] 表情
 

+ 250 - 110
WXUserReader.cs

@@ -18,6 +18,9 @@ using System.Xml.Linq;
 using WechatBakTool.Helpers;
 using WechatBakTool.Model;
 using System.Windows;
+using System.Net.Http;
+using System.Reflection.Metadata;
+using System.Threading;
 
 namespace WechatBakTool
 {
@@ -27,6 +30,8 @@ namespace WechatBakTool
         private UserBakConfig? UserBakConfig = null;
         private Hashtable HeadImgCache = new Hashtable();
         private Hashtable UserNameCache = new Hashtable();
+        private Hashtable EmojiCache = new Hashtable();
+        private HttpClient httpClient = new HttpClient();
         public WXUserReader(UserBakConfig userBakConfig) {
             string path = Path.Combine(userBakConfig.UserWorkspacePath, "DecDB");
             UserBakConfig = userBakConfig;
@@ -48,9 +53,21 @@ namespace WechatBakTool
             }
         }
 
+        private SQLiteConnection? getCon(string name)
+        {
+            if (DBInfo.ContainsKey(name))
+            {
+                return DBInfo[name];
+            }
+            else
+            {
+                return null;
+            }
+        }
+
         public void InitCache()
         {
-            SQLiteConnection con = DBInfo["Misc"];
+            SQLiteConnection? con = getCon("Misc");
             if (con == null)
                 return;
 
@@ -72,6 +89,101 @@ namespace WechatBakTool
             }
         }
 
+        public void EmojiCacheInit()
+        {
+            string emoji_path = Path.Combine(UserBakConfig!.UserWorkspacePath, "Emoji");
+            if (Directory.Exists(emoji_path))
+            {
+                string[] files = Directory.GetFiles(emoji_path);
+                foreach (string file in files)
+                {
+                    FileInfo fileInfo = new FileInfo(file);
+                    string[] names = fileInfo.Name.Split(".");
+                    if (!EmojiCache.ContainsKey(names[0]))
+                    {
+                        EmojiCache.Add(names[0], 1);
+                    }
+                }
+            }
+        }
+
+        public void PreDownloadEmoji()
+        {
+            if (UserBakConfig == null)
+                return;
+
+            HttpClientHandler handler = new HttpClientHandler() { AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate };
+            HttpClient httpClient = new HttpClient(handler);
+
+            List<WXMsg> msgs = GetTypeMsg("47");
+            int i = 0;
+
+            EmojiCacheInit();
+
+            foreach (var msg in msgs)
+            {
+                i++;
+                if (i % 5 == 0)
+                {
+                    // 每5次让下载线程休息1秒
+                    Thread.Sleep(1000);
+                }
+                try
+                {
+                    XmlDocument xmlDocument = new XmlDocument();
+                    xmlDocument.LoadXml(msg.StrContent);
+                    XmlNode? node = xmlDocument.SelectSingleNode("/msg/emoji");
+                    if (node != null)
+                    {
+                        if (node.Attributes != null)
+                        {
+                            string type = "";
+                            string md5 = "";
+                            string url = "";
+                            XmlNode? item = node.Attributes.GetNamedItem("type");
+                            type = item != null ? item.InnerText : "";
+
+                            item = node.Attributes.GetNamedItem("md5");
+                            md5 = item != null ? item.InnerText : "";
+
+                            item = node.Attributes.GetNamedItem("cdnurl");
+                            url = item != null ? item.InnerText : "";
+
+                            if (EmojiCache.ContainsKey(md5))
+                                continue;
+
+                            if (url == "")
+                                continue;
+
+                            else
+                            {
+                                string path = Path.Combine(UserBakConfig.UserWorkspacePath, "Emoji", md5 + ".jpg");
+                                try
+                                {
+                                    HttpResponseMessage res = httpClient.GetAsync(url).Result;
+                                    if (res.IsSuccessStatusCode)
+                                    {
+                                        using (FileStream fs = File.Create(path))
+                                        {
+                                            res.Content.ReadAsStream().CopyTo(fs);
+                                        }
+                                    }
+                                }
+                                catch (Exception ex)
+                                {
+
+                                }
+                            }
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+
+                }
+            }
+        }
+
         public byte[]? GetHeadImgCahce(string username)
         {
             if (HeadImgCache.ContainsKey(username))
@@ -87,7 +199,7 @@ namespace WechatBakTool
 
         public int[] GetWXCount()
         {
-            SQLiteConnection con = DBInfo["MicroMsg"];
+            SQLiteConnection? con = getCon("MicroMsg");
             if (con == null)
                 return new int[] { 0, 0 };
 
@@ -97,22 +209,19 @@ namespace WechatBakTool
             int msgCount = 0;
             for (int i = 0; i <= 99; i++)
             {
-                if (DBInfo.ContainsKey("MSG" + i.ToString()))
-                {
-                    con = DBInfo["MSG" + i.ToString()];
-                    if (con == null)
-                        return new int[] { userCount, 0 };
+                con = getCon("MSG" + i.ToString());
+                if (con == null)
+                    return new int[] { userCount, msgCount };
 
-                    query = "select count(*) as count from MSG";
-                    msgCount += con.Query<WXCount>(query)[0].Count;
-                }
+                query = "select count(*) as count from MSG";
+                msgCount += con.Query<WXCount>(query)[0].Count;
             }
             return new int[] { userCount, msgCount };
         }
 
         public ObservableCollection<WXContact> GetWXContacts(string? name = null,bool all = false)
         {
-            SQLiteConnection con = DBInfo["MicroMsg"];
+            SQLiteConnection? con = getCon("MicroMsg");
             if (con == null)
                 return new ObservableCollection<WXContact>();
             string query = @"select contact.*,session.strContent,contactHeadImgUrl.smallHeadImgUrl,contactHeadImgUrl.bigHeadImgUrl from contact 
@@ -146,13 +255,23 @@ namespace WechatBakTool
                 byte[]? imgBytes = GetHeadImgCahce(contact.UserName);
                 if (imgBytes != null)
                 {
-                    MemoryStream stream = new MemoryStream(imgBytes);
-                    BitmapImage bitmapImage = new BitmapImage();
-                    bitmapImage.BeginInit();
-                    bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
-                    bitmapImage.StreamSource = stream;
-                    bitmapImage.EndInit();
-                    contact.Avatar = bitmapImage;
+                    try
+                    {
+                        MemoryStream stream = new MemoryStream(imgBytes);
+                        BitmapImage bitmapImage = new BitmapImage();
+                        bitmapImage.BeginInit();
+                        bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
+                        bitmapImage.StreamSource = stream;
+                        bitmapImage.EndInit();
+                        bitmapImage.Freeze();
+                        contact.Avatar = bitmapImage;
+                    }
+                    catch
+                    {
+#if DEBUG
+                        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));
+#endif
+                    }
                 }
                 else
                     continue;
@@ -163,7 +282,7 @@ namespace WechatBakTool
 
         public List<WXUserImg>? GetUserImgs()
         {
-            SQLiteConnection con = DBInfo["MicroMsg"];
+            SQLiteConnection? con = getCon("MicroMsg");
             if (con == null)
                 return null;
             string query = "select * from contactHeadImgUrl";
@@ -172,107 +291,133 @@ namespace WechatBakTool
 
         public List<WXChatRoom>? GetWXChatRooms()
         {
-            SQLiteConnection con = DBInfo["MicroMsg"];
+            SQLiteConnection? con = getCon("MicroMsg");
             if (con == null)
                 return null;
             string query = "select * from ChatRoom";
             return con.Query<WXChatRoom>(query);
         }
+
+        public List<WXMsg> GetTypeMsg(string type)
+        {
+            List<WXMsg> tmp = new List<WXMsg>();
+            for (int i = 0; i <= 99; i++)
+            {
+                SQLiteConnection? con = getCon("MSG" + i.ToString());
+                if (con == null)
+                    return tmp;
+
+                string query = "select * from MSG where Type=?";
+                List<WXMsg> wXMsgs = con.Query<WXMsg>(query, type);
+                tmp.AddRange(wXMsgs);
+            }
+            return tmp;
+        }
         public List<WXMsg>? GetWXMsgs(string uid,string msg = "")
         {
             List<WXMsg> tmp = new List<WXMsg>();
             for (int i = 0; i <= 99; i++)
             {
-                if(DBInfo.ContainsKey("MSG" + i.ToString()))
+                SQLiteConnection? con = getCon("MSG" + i.ToString());
+                if (con == null)
+                    return tmp;
+
+                List<WXMsg>? wXMsgs = null;
+                if (msg == "")
                 {
-                    SQLiteConnection con = DBInfo["MSG" + i.ToString()];
-                    if (con == null)
-                        return tmp;
+                    string query = "select * from MSG where StrTalker=?";
+                    wXMsgs = con.Query<WXMsg>(query, uid);
+                }
+                else if (uid == "")
+                {
+                    string query = "select * from MSG where StrContent like ?";
+                    wXMsgs = con.Query<WXMsg>(query, string.Format("%{0}%", msg));
+                }
+                else
+                {
+                    string query = "select * from MSG where StrTalker=? and StrContent like ?";
+                    wXMsgs = con.Query<WXMsg>(query, uid, string.Format("%{0}%", msg));
+                }
 
-                    List<WXMsg>? wXMsgs = null;
-                    if (msg == "")
-                    {
-                        string query = "select * from MSG where StrTalker=?";
-                        wXMsgs = con.Query<WXMsg>(query, uid);
-                    }
-                    else if(uid == "")
-                    {
-                        string query = "select * from MSG where StrContent like ?";
-                        wXMsgs = con.Query<WXMsg>(query, string.Format("%{0}%", msg));
-                    }
-                    else
+                foreach (WXMsg w in wXMsgs)
+                {
+                    if (UserNameCache.ContainsKey(w.StrTalker))
                     {
-                        string query = "select * from MSG where StrTalker=? and StrContent like ?";
-                        wXMsgs = con.Query<WXMsg>(query, uid, string.Format("%{0}%", msg));
+                        WXContact? contact = UserNameCache[w.StrTalker] as WXContact;
+                        if (contact != null)
+                        {
+                            if (contact.Remark != "")
+                                w.NickName = contact.Remark;
+                            else
+                                w.NickName = contact.NickName;
+                        }
+
                     }
 
-                    foreach (WXMsg w in wXMsgs)
+                    if (uid.Contains("@chatroom"))
                     {
-                        if (UserNameCache.ContainsKey(w.StrTalker))
-                        {
-                            WXContact? contact = UserNameCache[w.StrTalker] as WXContact;
-                            if (contact != null)
-                            {
-                                if (contact.Remark != "")
-                                    w.NickName = contact.Remark;
-                                else
-                                    w.NickName = contact.NickName;
-                            }
-                        }
+                        string userId = "";
 
-                        if (uid.Contains("@chatroom"))
-                        {
-                            string userId = "";
+                        if (w.BytesExtra == null)
+                            continue;
 
-                            if (w.BytesExtra == null)
-                                continue;
+                        string sl = BitConverter.ToString(w.BytesExtra).Replace("-", "");
 
-                            string sl = BitConverter.ToString(w.BytesExtra).Replace("-", "");
+                        ProtoMsg protoMsg;
+                        using (MemoryStream stream = new MemoryStream(w.BytesExtra))
+                        {
+                            protoMsg = ProtoBuf.Serializer.Deserialize<ProtoMsg>(stream);
+                        }
 
-                            ProtoMsg protoMsg;
-                            using (MemoryStream stream = new MemoryStream(w.BytesExtra))
+                        if (protoMsg.TVMsg != null)
+                        {
+                            foreach (TVType _tmp in protoMsg.TVMsg)
                             {
-                                protoMsg = ProtoBuf.Serializer.Deserialize<ProtoMsg>(stream);
+                                if (_tmp.Type == 1)
+                                    userId = _tmp.TypeValue;
                             }
+                        }
 
-                            if(protoMsg.TVMsg != null)
-                            {
-                                foreach(TVType _tmp in protoMsg.TVMsg)
-                                {
-                                    if (_tmp.Type == 1)
-                                        userId = _tmp.TypeValue;
-                                }
-                            }
-                            
 
-                            if (!w.IsSender)
+                        if (!w.IsSender)
+                        {
+                            if (UserNameCache.ContainsKey(userId))
                             {
-                                if(UserNameCache.ContainsKey(userId))
-                                {
-                                    WXContact? contact = UserNameCache[userId] as WXContact;
-                                    if (contact != null)
-                                        w.NickName = contact.Remark == "" ? contact.NickName : contact.Remark;
-                                }
-                                else
-                                {
-                                    w.NickName = userId;
-                                }
+                                WXContact? contact = UserNameCache[userId] as WXContact;
+                                if (contact != null)
+                                    w.NickName = contact.Remark == "" ? contact.NickName : contact.Remark;
                             }
                             else
                             {
-                                w.NickName = "我";
+                                w.NickName = userId;
                             }
                         }
-                        
-                        tmp.Add(w);
                     }
+                    if (w.IsSender)
+                        w.NickName = "我";
+
+                    if (w.Type != 1)
+                    {
+                        if (w.Type == 10000)
+                        {
+                            w.Type = 1;
+                            w.NickName = "系统消息";
+                            w.StrContent = w.StrContent.Replace("<revokemsg>", "").Replace("</revokemsg>", "");
+                        }
+                        else
+                        {
+                            w.StrContent = "[界面未支持格式]Type=" + w.Type;
+                        }
+                    }
+                    tmp.Add(w);
+
                 }
             }
             return tmp;
         }
         public List<WXSessionAttachInfo>? GetWXMsgAtc()
         {
-            SQLiteConnection con = DBInfo["MultiSearchChatMsg"];
+            SQLiteConnection? con = getCon("MultiSearchChatMsg");
             if (con == null)
                 return null;
 
@@ -287,7 +432,7 @@ namespace WechatBakTool
         }
         public WXSessionAttachInfo? GetWXMsgAtc(WXMsg msg)
         {
-            SQLiteConnection con = DBInfo["MultiSearchChatMsg"];
+            SQLiteConnection? con = getCon("MultiSearchChatMsg");
             if (con == null)
                 return null;
 
@@ -316,17 +461,14 @@ namespace WechatBakTool
         {
             for (int i = 0; i <= 99; i++)
             {
-                if(DBInfo.ContainsKey("MediaMSG" + i.ToString()))
-                {
-                    SQLiteConnection con = DBInfo["MediaMSG" + i.ToString()];
-                    if (con == null)
-                        continue;
-
-                    string query = "select * from Media where Reserved0=?";
-                    List<WXMediaMsg> wXMsgs = con.Query<WXMediaMsg>(query, msg.MsgSvrID);
-                    if (wXMsgs.Count != 0)
-                        return wXMsgs[0];
-                }
+                SQLiteConnection? con = getCon("MediaMSG" + i.ToString());
+                if (con == null)
+                    continue;
+
+                string query = "select * from Media where Reserved0=?";
+                List<WXMediaMsg> wXMsgs = con.Query<WXMediaMsg>(query, msg.MsgSvrID);
+                if (wXMsgs.Count != 0)
+                    return wXMsgs[0];
             }
             return null;
         }
@@ -443,23 +585,21 @@ namespace WechatBakTool
             List<WXMsgGroup> g = new List<WXMsgGroup>();
             for (int i = 0; i <= 99; i++)
             {
-                if (DBInfo.ContainsKey("MSG" + i.ToString()))
-                {
-                    SQLiteConnection con = DBInfo["MSG" + i.ToString()];
-                    if (con == null)
-                        return g;
+                SQLiteConnection? con = getCon("MSG" + i.ToString());
+                if (con == null)
+                    return g;
 
-                    string query = "select StrTalker,Count(localId) as MsgCount from MSG GROUP BY StrTalker";
-                    List<WXMsgGroup> wXMsgs = con.Query<WXMsgGroup>(query);
-                    foreach (WXMsgGroup w in wXMsgs)
-                    {
-                        WXMsgGroup? tmp = g.Find(x => x.UserName == w.UserName);
-                        if (tmp == null)
-                            g.Add(w);
-                        else
-                            tmp.MsgCount += g.Count;
-                    }
+                string query = "select StrTalker,Count(localId) as MsgCount from MSG GROUP BY StrTalker";
+                List<WXMsgGroup> wXMsgs = con.Query<WXMsgGroup>(query);
+                foreach (WXMsgGroup w in wXMsgs)
+                {
+                    WXMsgGroup? tmp = g.Find(x => x.UserName == w.UserName);
+                    if (tmp == null)
+                        g.Add(w);
+                    else
+                        tmp.MsgCount += g.Count;
                 }
+
             }
             return g;
         }

+ 3 - 3
WechatBakTool.csproj

@@ -6,9 +6,9 @@
     <Nullable>enable</Nullable>
     <UseWPF>true</UseWPF>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
-    <AssemblyVersion>0.9.2.0</AssemblyVersion>
-    <FileVersion>0.9.2.0</FileVersion>
-    <Version>0.9.2.0</Version>
+    <AssemblyVersion>0.9.3.0</AssemblyVersion>
+    <FileVersion>0.9.3.0</FileVersion>
+    <Version>0.9.3.0</Version>
   </PropertyGroup>
 
   <ItemGroup>